<?xml version="1.0" encoding="utf-8" standalone="yes"?><?xml-stylesheet href="/rss.xsl" type="text/xsl"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Frederick Vanbrabant's Delirious Rantings</title><link>https://frederickvanbrabant.com/</link><description>Full-text dispatch from Frederick Vanbrabant's Delirious Rantings</description><language>en-us</language><lastBuildDate>Mon, 09 Mar 2026 06:35:31 +0000</lastBuildDate><atom:link href="https://frederickvanbrabant.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Governance: Documentation as a Knowledge Network</title><link>https://frederickvanbrabant.com/blog/2026-03-07-governance-documentation-as-a-knowledge-network/</link><pubDate>Sat, 07 Mar 2026 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2026-03-07-governance-documentation-as-a-knowledge-network/</guid><description>&lt;p&gt;If you’ve known me for long enough, there will be a point where I&amp;rsquo;m going to pitch you the concept of &lt;a href="https://obsidian.md/"
target="_blank"
&gt;Obsidian&lt;/a&gt;. I adore that program&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;, I basically live my life in it. Everything is connected, and ideas just bubble up on their own.&lt;/p&gt;
&lt;p&gt;That love for Obsidian is always amplified when I have to look up something on an organisational documentation platform. I can never find anything, it&amp;rsquo;s always out of date, and it has conflicting ideas all over the place. I&amp;rsquo;m pretty sure you can relate (if you even have one).&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>If you’ve known me for long enough, there will be a point where I&rsquo;m going to pitch you the concept of <a href="https://obsidian.md/"




 target="_blank"
 


>Obsidian</a>. I adore that program<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, I basically live my life in it. Everything is connected, and ideas just bubble up on their own.</p>
<p>That love for Obsidian is always amplified when I have to look up something on an organisational documentation platform. I can never find anything, it&rsquo;s always out of date, and it has conflicting ideas all over the place. I&rsquo;m pretty sure you can relate (if you even have one).</p>
<p>Using applications like Obsidian for organisations is not workable, they aren&rsquo;t built for multi-user collaboration; it&rsquo;s not their product focus. And the applications I come across in organisations are also not bad. Tools like Confluence, Notion, Slite, and even things like SharePoint sites are not the problem. They are good tools.</p>
<p>The issue is the way they are set up. An endless, mindless dump of folders with duplicate and contradictory explanations, sometimes even just a PowerPoint slide deck that might or might not be relevant.</p>
<p>And I&rsquo;m always amazed about the lack of strategy in setting something like this up. How is it that I can find where the third King of the Belgians was born in a few clicks yet finding out what our expense policy is about is something you would rather ask a colleague, then look for on the organisational wiki?</p>
<p>I&rsquo;ve done a lot of research about this over the years, and I would like to share my ideas on how to set up a documentation store.</p>
<p>This is going to be a two part post. The first one is the general outline and philosophy. The second part is about structuring project governance documentation.</p>
<h2 id="the-knowledge-graph">The knowledge graph</h2>
<p>A lot of organisational wikis are stored in folder structures. Something like this:</p>
<pre tabindex="0"><code>IT Department General
  ↳ 00_2025 Archives
  ↳ 01_Projects
	  ↳ Project Phoenix Redesign
	    ↳ Architecture
	    ↳ Data
  ↳ 02_Governance and Compliance
	  ↳ Move to cloud
	    ↳ Cloud_Usage_Policy_2026.pdf
	    ↳ License_Agreement.pdf
</code></pre><p>This mimics a file system and in the case of SharePoint is also often just a copy and paste from one. A bit of a dumping ground where you work from a file folder and try not to go out of it. Everything is trapped in its own container.</p>
<p>The idea of a knowledge graph goes in the opposite direction. In its rawest form, you do away with folders and structure altogether (we are not going to do that, but it makes the concept easier to introduce).</p>
<p>Wikipedia is actually a nice example of this. If you want to find the third king of the Belgians (<a href="https://en.wikipedia.org/wiki/Albert_I_of_Belgium"




 target="_blank"
 


>Albert I</a>) you could go to the “Belgium” page and find a link there to said king. But you could also go through the page of past kings, or even the “Summer Olympic Games” main page.</p>
<p>That’s the beautiful concept behind Knowledge Graphs, they create organic links with relevant information without the need for you to set it up.</p>
<p>You know, the way the internet works.</p>
<h2 id="the-moc-the-map-of-content">The MOC: The Map of Content</h2>
<p>In this state it&rsquo;s very chaotic. It would be a huge list of coupled documents, but you&rsquo;d still have a hard time actually finding what you&rsquo;d need.</p>
<p>That&rsquo;s where <strong>MOC</strong>&rsquo;s (<strong>M</strong>ap <strong>o</strong>f <strong>C</strong>ontent) come in. These are landing pages that help you on your way. Again, very much like Wikipedia. To go to a topic you go to one of the main ideas of the topic, and it will guide you there.</p>
<p>Belgium could be a MOC for guiding you towards its old kings, but so could World War I.</p>
<p>These pages can also include information themselves to introduce you towards the bigger concept. A MOC of Belgium would not direct you to a Belgium detail page, it would serve as both the main topic and the launch pad towards the deeper topics.</p>

<pre class="mermaid">
graph TD
  ALBERT["Albert I of Belgium"]

  BELGIUM["Belgium MOC"] --> ALBERT
  KINGS["Kings of Belgium MOC"] --> ALBERT
  WWI["World War I MOC"] --> ALBERT
  OLYMPICS["Summer Olympics MOC"] --> ALBERT

  ALBERT --> REIGN["His Reign"]
  ALBERT --> MILITARY["Military Career"]
  ALBERT --> LEGACY["Legacy & Death"]
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><h2 id="atomic-documentation">Atomic Documentation</h2>
<p>The problem, however, is that your organisation is not going to have the thousands of maintainers that a place like Wikipedia has. So we are going to have to deviate slightly from how Wikipedia does things<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<p>The issue with long articles is that not a lot of people find the motivation to write them. It takes a lot of work to write a decent long explanation of a concept<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.</p>
<p>It’s also a bit daunting to jump into a very long article and read the entire thing when you are actually just in need for a small part of the information.</p>
<p>There is also the point of keeping this up to date. If you keep explaining the full concept of how your cloud strategy works on multiple pages, you&rsquo;re going to have to rework (and search) for all of these references every time the strategy changes. A classic example here is the architecture principles rewritten in every project.</p>
<p>This is where Atomic Documentation comes in: one concept per page. Reference the rest.</p>
<p>We borrow some concepts from the software development world:</p>
<ul>
<li>Single Source of Truth: If a Business Capability is defined once, it should be referenced everywhere, never re-typed.</li>
<li>Transclusion: Confluence is pretty good at this for example. You can embed parts of a different document in the current document. I often find it better to just link, but the concept is the same. Make changes at the source and let the information drip upstream. Everything stays up to date.</li>
<li>Rabbit Hole Exploration: Designing documentation that encourages exploration. Allowing a reader to start at a high level and drill down into technical specifics via links. This way you can bring information at different resolutions.</li>
</ul>
<p>By following through on the documentation in a project you might stumble upon the architectural principles of the organisation. A page that you might not have been looking for.</p>

<pre class="mermaid">
flowchart TD
    ProjectA[Project Phoenix] --> App[SAP ERP]
    ProjectB[Project Unicorn] --> App

    App --> Cloud[Cloud Strategy]
    App --> Architecture[Architecture Principles]

    subgraph SSOT [Single Source of Truth]
    Cloud
    Architecture
    end

    style SSOT fill:#e1f5fe,stroke:#01579b,stroke-dasharray: 5 5
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><h2 id="metadata">Metadata</h2>
<p>Adding metadata is not strictly necessary, but it&rsquo;s a nice touch. It also typically comes out of the box with most of these applications so it&rsquo;s not too much work to set up.</p>
<p>I strongly believe that old data is not per se bad data. It&rsquo;s not because the data is not refreshed every year that it is less valid<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>. That said, I do like a date for when it was added and when it was last updated. This can come in handy for information that is date sensitive. Think legal policy or technology policy that is linked to specific tools.</p>
<p>An author field is also a nice touch. It always helps when the data is scarce, and you want to know more.</p>
<p>I&rsquo;m not sure about a status field. Having a label that the documentation is outdated could be nice. One that says that it&rsquo;s work in progress is less useful for me. In my mind, all information is a work in progress…</p>
<h2 id="organized-chaos">Organized chaos</h2>
<p>We have our small and numerous notes, we have our MOCs, but we would still like some extra structure. The next part goes a bit against the grain of the knowledge graph concept, but it&rsquo;s needed in an organisation.</p>
<p>Leaving a dumping ground with MOCs and notes is too intimidating for new users to drop into. You&rsquo;re never going to get that adopted. You&rsquo;re going to need folders.</p>
<p>This is the current structure I&rsquo;ve landed on:</p>
<ul>
<li>Projects</li>
<li>Applications</li>
<li>Processes</li>
<li>Resources</li>
<li>Archive</li>
</ul>
<h3 id="projects">Projects</h3>
<p>I&rsquo;m going to discuss this one in the next post, but I guess you might have a vague idea of what&rsquo;s going on in there</p>
<h3 id="applications">Applications</h3>
<p>A lot of people go to these documentation sites to find information about applications. This is a core concept.</p>
<p>Under the Applications folder you can create a folder per application. Each with their own MOC.</p>
<pre tabindex="0"><code>Applications
  ↳ Applications_MOC.md (The Software Catalog)
  ↳ SAP
    ↳ SAP MOC.md
    ↳ Licence
	    ↳ SAP_License_Agreement_2024.pdf
	↳ Diagrams
		↳ SAP_Network_Topology.png
		↳ SAP_Network_Topology.vsdx
  ↳ Salesforce
    ↳ Salesforce MOC.md
    ↳ Salesforce_Security_Model.md
</code></pre><p>You might have scrolled up to compare it to the example of the “old way” of doing it and thinking what&rsquo;s the difference here?</p>
<p>Well, the most important part is that we are giving people an onboarding ramp. We don&rsquo;t actually care about this folder structure, but we use it as a familiar starting point. Once they are in, and they start following links they will no longer look at the folder structure as they have landed after two clicks in at a very different spot.</p>
<h3 id="processes">Processes</h3>
<p>The same idea as the applications. A lot of questions will be: How do I do X? well you can go in the Process folder and explore your starting point.</p>
<pre tabindex="0"><code>Processes
  ↳ Processes_MOC.md (The Value Stream Map)
  ↳ Order to Cash
	  ↳ Order to Cash MOC.md
	  ↳ ... deeper in till you hit low level processes
</code></pre><p>The same idea here. When you talk about some deeper processes, you can link to different concepts, often applications. You might also end up in these structures from Applications or maybe even a Capability map. Speaking of which.</p>
<h3 id="resources">Resources</h3>
<p>This is a bit of a dumping ground for the other things. A capability map with the levels would work well here, but also architectural principles or an expense policy.</p>
<p>You would still use MOCs, you would still link everything over the documentations. It&rsquo;s not always clear what is where. For example, does a Microsoft licence agreement fit here under procurement documents, or rather under technology licences? It doesn&rsquo;t really matter, again, this folder structure is not the point, it&rsquo;s the connections.</p>
<h3 id="archive">Archive</h3>
<p>You could also delete, but then you have broken links. If your software allows it you could add a banner to everything under this section. This part will also be important in the next article about the project documentation.</p>

<pre class="mermaid">
graph TD
    Root((Start)) --> F_App(Folder: Applications)
    Root --> F_Proc(Folder: Processes)
    Root --> F_Res(Folder: Resources)

    F_App --> SAP_MOC[SAP MOC]
    subgraph SAP_Subfolders [SAP Folder Structure]
        SAP_MOC -.-> F_Lic(Subfolder: Licence)
        F_Lic --> Lic_File[SAP Licence Agreement]
    end

    F_Proc --> O2C_MOC[Order to Cash MOC]
    O2C_MOC -- contextual link --> SAP_MOC
    F_Res --> Contract_MOC[Contracts MOC]
    Contract_MOC -- contextual link --> Lic_File

    %% Styling for clarity
    classDef MOCPage fill:#fff4dd,stroke:#d4a017,stroke-width:2px;
    classDef Structure fill:#f5f5f5,stroke:#999,stroke-dasharray: 5 5;
    classDef SingleFile fill:#e1f5fe,stroke:#0277bd,stroke-width:2px;

    class SAP_MOC,O2C_MOC,Contract_MOC MOCPage;
    class SAP_Subfolders,F_Lic Structure;
    class Lic_File SingleFile;
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><h2 id="the-elephant-in-the-room">The Elephant in the Room</h2>
<p>Your structure might be very solid, but the fact still remains of garbage in, garbage out.</p>
<p>And that brings us to LLMs.</p>
<p>You could just generate all these documents with your favourite LLM, but then you will have just wasted your time (and money). In my experience people don&rsquo;t read auto generated documentation. It should take (a lot) more time to write than to read. And I understand that writing documentation is not the favourite pastime of a lot of people. <sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> And you are never going to be able to stop people from auto generating documentation, but with Atomic Notes the temptation should be lowered.</p>
<p>The main problem with this setup is that it&rsquo;s not compatible with auto generated documentation. If you don&rsquo;t add links to other parts of the documentation, the entire system falls apart. Either the LLM stuff can&rsquo;t be discovered or it floods the system and drowns out the rest of the setup.</p>
<p>I don&rsquo;t think there is a solution to this, but that&rsquo;s also the same with all documentation in general, the signal-to-noise ratio is getting out of hand.</p>
<p>You can put organisational guidelines and training forward, use a LLM to support writing and not replace it. But that also requires people to care enough to follow those guidelines.</p>
<h2 id="living-documentation">Living documentation</h2>
<p>I see documentation (and information in general) as an organic thing. This might just be the architect in me who likes to think in interconnected systems, but the value is often in the relationship, and not the individual piece of information.</p>
<p>That&rsquo;s the entire philosophy of this setup. Interconnected information with signposts to point you in the right way.</p>
<p>We use small and easily scannable documents to quickly communicate one piece of information. Once we are dragging in different concepts we link, or create new small pieces of information. And encourage people to do deep dives if the time (and interest) allows it. If not, people still have a high level overview of what they need.</p>
<p>Stay tuned for the next part in two weeks where we dive into project documentation.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>All of these blog post start out as Obsidian notes&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Actually not entirely, if you go to the wayback machine and look at archives from the early days of Wikipedia you can see this concept in action&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>I work multiple days on posts like this (not fulltime)&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>I have the same idea about software. It&rsquo;s perfectly possible that a Github page with a last commit of 5 years ago is just &ldquo;finished software&rdquo;&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>I&rsquo;m very much a rare breed&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Systems Thinking in Enterprise Architecture</title><link>https://frederickvanbrabant.com/blog/2026-02-20-systems-thinking-in-enterprise-architecture/</link><pubDate>Fri, 20 Feb 2026 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2026-02-20-systems-thinking-in-enterprise-architecture/</guid><description>&lt;p&gt;I first learned of systems thinking in the domain of city planning, and that is apparently also where the idea comes from. It was described to me in the context of building new residential buildings and effects on local bird populations.&lt;/p&gt;
&lt;p&gt;Birds don’t always perceive glass clearly, especially when it&amp;rsquo;s a tall apartment building and on their flight path. So there are birds that think they can pass under an arch, yet instead fly straight into a glass wall.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>I first learned of systems thinking in the domain of city planning, and that is apparently also where the idea comes from. It was described to me in the context of building new residential buildings and effects on local bird populations.</p>
<p>Birds don’t always perceive glass clearly, especially when it&rsquo;s a tall apartment building and on their flight path. So there are birds that think they can pass under an arch, yet instead fly straight into a glass wall.</p>
<p>However, some of these buildings are also perfectly suited for nesting and resting places.</p>
<p>Now, I&rsquo;m sure there are some meetings that architects and city planners have on the impact of their design on the local bird population. It&rsquo;s probably not their clients first item on the list in ordering new buildings and spaces.</p>
<p>Another example is adding trees to streets to lower city temperature<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. A lower temperature in a greener city might have an impact on the sale of beverages in the local cafés.</p>
<p>All of these things are nearly impossible to map. It&rsquo;s too complicated a problem<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. The same is true for organizations and enterprise architecture. Yet it&rsquo;s an enterprise architect&rsquo;s job to map out an organization.</p>
<h2 id="the-rumsfeld-matrix">The Rumsfeld Matrix</h2>
<p>In strategic planning there is a framework called the Rumsfeld Matrix. It&rsquo;s attributed to Donald Rumsfeld, yes, <em>that</em>, Donald Rumsfeld. But in reality it&rsquo;s an older concept that was used before in the late 1960s.</p>
<p>The idea of the matrix is that you map out what you know and what you don&rsquo;t know. That sounds very contradictory, how can you know what you don&rsquo;t know, but you abstract it. We do this to ground ourselves and don&rsquo;t lose the plot while we are setting up a strategy.</p>
<p>Every matrix has four quadrants, here it&rsquo;s no different, and in this case they are:</p>
<h3 id="the-known-knowns">The Known Knowns</h3>
<p>This is what we know and what we have mapped. We have a full view of where we can find the data, what it looks like, how it arrived there, and how we can use it.</p>
<p>This makes up most of the diagrams an Enterprise Architect makes. Examples here are the CMDB, API documentation, Organizational charts …</p>
<h3 id="the-known-unknowns">The Known Unknowns</h3>
<p>You always have a list of things you want to map out, but haven&rsquo;t got around to yet. Think about a backlog of technical debt, or business processes that aren&rsquo;t mapped out yet, but you vaguely know what they do. You know where you can go look for them and how you could use the information, you just don&rsquo;t know the actual data itself. This also includes information that is too simplified to fully make use of.</p>
<h3 id="the-unknown-knowns">The Unknown Knowns</h3>
<p>Here we have the information that the “system” knows, but you don&rsquo;t. Categorized here is shadow IT for example, or a weird workflow the COBOL developer uses in some legacy system to make sure the accounts work.</p>
<p>The system performs the task, but the documentation (and the architect) is unaware of how.</p>
<p>These are often rabbit holes that come on your path when you are looking into systems. The skeletons in the closet.</p>
<h3 id="the-unknown-unknowns">The Unknown Unknowns</h3>
<p>Now we have the examples from the intro. Emerging situations that happen when two unrelated systems interact for the first time. Things that are typically results of factors way too complicated to actually map.</p>

<pre class="mermaid">
quadrantChart

title The Rumsfeld Matrix
x-axis "Unaware of the information" --> "Aware of the information"
y-axis "Not accurate information" --> "Accurate information"

quadrant-1 Known Knowns
quadrant-2 Unknown Knowns
quadrant-3 Unknown Unknowns
quadrant-4 Known Unknowns

Market Trends: [0.85, 0.2]
Legacy Code: [0.9, 0.10]
TOC Assets: [0.75, 0.35]
CMDB: [0.7, 0.7]
API Documentation: [0.85, 0.85]
Organizational Chart: [0.74, 0.90]
Shadow IT: [0.2, 0.85]
Tribal Knowledge: [0.15, 0.9]
Cascade Failures: [0.2, 0.2]
Market Pivots: [0.15, 0.15]
Cultural Feedback: [0.3, 0.3]
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><h2 id="causal-loop-diagrams">Causal Loop Diagrams</h2>
<p>Remember in the intro that I said it&rsquo;s impossible to map out an Unknown Unknown? Well you kind of can.</p>
<p>Not in advance, mind you, more in a post-mortem sense.</p>
<p>The concept here is that you go over the events that took place like a script of a movie. Situation per situation. Then later when you have mapped that out, it could function as lessons learned for future strategic decisions.</p>
<p>In general, you have two kinds of loops.</p>
<h3 id="reinforcing-loops">Reinforcing Loops</h3>
<p>You can see them as snowball effects, they amplify themselves. Both negatively and positively.</p>
<p>You can have a “success to the successful” loop where positive change is reinforced by more positive change:</p>

<pre class="mermaid">
	flowchart TD
	A[Well working Platform] -->|"attracts"| B[More Users]
	B -->|"generates"| C[More Funding]
	C -->|"enables"| D[Better Features]
	D -->|"drives"| A
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><p>But there is also the “death spiral” where the opposite is true:</p>

<pre class="mermaid">
flowchart TD
A[Pressure to Deliver] -->|"forces"| B[Quick & Dirty Fixes]
B -->|"accumulates into"| C[Higher Complexity]
C -->|"causes"| D[Slower Delivery]
D -->|"escalates"| A
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><h3 id="balancing-loops">Balancing Loops</h3>
<p>These loops seek stability or a target. They resist change, which is often why digital transformations fail. Death spirals are definitely something to avoid, but this status quo can be just as detrimental to your organization.</p>
<p>An example here is the typical modernization project:</p>

<pre class="mermaid">
flowchart TD
A[New Innovation Attempt] -->|"requires"| B[Integration with Legacy Systems]
B -->|"triggers"| C[Increased Maintenance Effort]
C -->|"consumes"| D[Decreased Time for Innovation]
D -->|"suppresses"| A
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><h2 id="a-map-is-not-the-territory">A map is not the territory</h2>
<p>I really like that quote. It&rsquo;s not mine, it&rsquo;s <a href="https://en.wikipedia.org/wiki/Alfred_Korzybski"




 target="_blank"
 


>Alfred Korzybski</a>. A diagram or map is only a representation of what you actually put on there. An abstraction. Even with the most detailed map in the world, you will still only show what you know.</p>
<p>And that&rsquo;s a good thing.</p>
<p>What I&rsquo;m saying is that the goal shouldn&rsquo;t be to map out all the things<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>. There is no way you&rsquo;re going to be able to do that. You should focus on the things that give value and make abstractions when there is no clear value.</p>
<p>I&rsquo;m not convinced Causal Loop Diagrams actually are all that useful as the parameters of your strategy will always keep changing, and even in the case of these diagrams you are making assumptions and abstractions.</p>
<p>It is however very important to be mindful that there are a lot of things happening in an organization that you cannot be aware of. And shouldn&rsquo;t be aware of. This keeps you out of the false sense of knowledge when making strategy.</p>
<p>PS: as a reader exercise I challenge you to think where AI agents and LLM&rsquo;s are located in the matrix. Is an LLM a &lsquo;Known Unknown&rsquo; (we know it&rsquo;s there but don&rsquo;t know what it will output) or an &lsquo;Unknown UnKnown&rsquo; (It&rsquo;s a black box, and we have no real way to look inside)? I&rsquo;ll leave that to your next architecture review meeting.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://stories.kuleuven.be/en/stories/cooler-city-greener-city"




 target="_blank"
 


>https://stories.kuleuven.be/en/stories/cooler-city-greener-city</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://frederickvanbrabant.com/blog/2025-01-17-turning-complexity-into-manageable-complication/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2025-01-17-turning-complexity-into-manageable-complication/</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://frederickvanbrabant.com/blog/2025-02-21-mapping-out-an-organization-is-a-massive-task/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2025-02-21-mapping-out-an-organization-is-a-massive-task/</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>The middle ground between canonical models and data mesh</title><link>https://frederickvanbrabant.com/blog/2026-02-06-the-middle-ground-between-canonical-models-and-data-mesh/</link><pubDate>Wed, 04 Feb 2026 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2026-02-06-the-middle-ground-between-canonical-models-and-data-mesh/</guid><description>&lt;p&gt;Some years ago I worked with a scale-up that was really focused on the way they handled data in their product. They extensively argued over data language, had value objects everywhere, explicit models and, even hexagonal architecture.&lt;/p&gt;
&lt;p&gt;It was a cool place to work, with a lot of smart people.&lt;/p&gt;
&lt;p&gt;At some point they started to talk about standardizing their data transfer objects, the data that flows over the API connections, in these common models. The idea was that there would be a single Invoice, User, Customer concept that they can document, standardize and share over their entire application landscape.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>Some years ago I worked with a scale-up that was really focused on the way they handled data in their product. They extensively argued over data language, had value objects everywhere, explicit models and, even hexagonal architecture.</p>
<p>It was a cool place to work, with a lot of smart people.</p>
<p>At some point they started to talk about standardizing their data transfer objects, the data that flows over the API connections, in these common models. The idea was that there would be a single Invoice, User, Customer concept that they can document, standardize and share over their entire application landscape.</p>
<p>Everyone had the same idea of what a user was, from database engineer all the way to the sales people. They called it unambiguous language.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<h2 id="the-canonical-data-model">The Canonical Data Model</h2>
<p>What they were inventing is now known as a Canonical Data Model. A centralized data model that you reuse for everything. And to be fair to that team, there are companies that make this work. Especially in highly regulated environments you can see this in play for some objects. In banks or medical companies it&rsquo;s not uncommon to have data contracts that need to encapsulate a ledger or medical checks.</p>
<p>It also brings other upsides, regarding onboarding and integrations it&rsquo;s rather sweet. The Payroll team needs the customer data, everyone knows exactly what they can expect and what they need to send.</p>

<pre class="mermaid">
graph TD
    subgraph "Canonical Data Model"
        CDM[<b>User Model</b>]
    end

	CDM --> Billing[Billing Service]
	CDM --> Sales[Sales Service]
	CDM --> Shipping[Shipping Service]
	CDM --> Support[Support Service]
	CDM --> Marketing[Marketing Service]
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><p>The problem is, however, that it&rsquo;s a horrible setup.</p>
<h2 id="bounded-context">Bounded context</h2>
<p>When that team was often talking about domain driven design concepts (value objects, unambiguous language) they seemed to miss the domain part. More specifically, the bounded context.</p>
<p>The bounded context is the lens you use to look at a concept. Let me explain in the most technical way possible, with a JSON object.</p>
<p>Say you want to kickstart the idea of a CDM as someone working in the domain of billing, and you propose your Customer object. <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JSON" data-lang="JSON"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;40bb524a-3e0e-4a4a-93df-399819b3fd68&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Acme Corp&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;vat_number&#34;</span><span class="p">:</span> <span class="s2">&#34;BE0123456789&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Now you get some people from the other departments together and ask them, what do you think?</p>
<p>After a few workshops your Customer object is now:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JSON" data-lang="JSON"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;40bb524a-3e0e-4a4a-93df-399819b3fd68&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Acme Corp&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;vat_number&#34;</span><span class="p">:</span> <span class="s2">&#34;BE0123456789&#34;</span><span class="p">,</span> <span class="c1">// Billing needs this
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nt">&#34;credit_limit&#34;</span><span class="p">:</span> <span class="mf">50000.00</span><span class="p">,</span> <span class="c1">// Sales needs this
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nt">&#34;delivery_instructions&#34;</span><span class="p">:</span> <span class="s2">&#34;Back door&#34;</span><span class="p">,</span> <span class="c1">// Shipping needs this
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nt">&#34;support_tier&#34;</span><span class="p">:</span> <span class="s2">&#34;Gold&#34;</span><span class="p">,</span> <span class="c1">// Support needs this
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nt">&#34;marketing_opt_in&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// Marketing needs this
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nt">&#34;last_invoice_date&#34;</span><span class="p">:</span> <span class="s2">&#34;2024-01-01&#34;</span><span class="p">,</span> <span class="c1">// Data is leaking
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nt">&#34;crm_lead_source&#34;</span><span class="p">:</span> <span class="s2">&#34;Conference&#34;</span> <span class="c1">// Why is this in the Shipping app?
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p><em>Congratulations, you’ve just built a God Object.</em></p>
<p>A customer can mean a lot of things to a lot of different people. This is the bounded context. For a sales person a customer is a person that buys things, for a support person they are a person that needs help. They both have different lenses.</p>
<p>Now if we keep following the Canonical Data Model, this Customer object will keep on growing. Every week there will be a committee that decides what fields need to be added (you cannot remove fields as that impacts your applications).</p>
<p>In the end you have a model that nobody owns, has too much information for everyone and requires constant updating.</p>
<h2 id="enter-the-data-mesh">Enter the Data Mesh</h2>
<p>A way to solve this, is data mesh. This takes the concept of bounded context as a core principle.</p>
<p>In the context of this discussion, data mesh sees data as a product. A product that is maintained by the people in the domain. That means that a customer in the Billing domain only maintains and focuses on the Billing domain logic in the customer concept.</p>
<p>They are responsible for the quality and contract but not for the representation. That means in practice that they can decide how a VAT number is structured. But not how the Sales team needs to format said model. They have no control or interest in how other domains use the data.</p>
<p>How we do this is through a concept called anti-corruption layers. These are transformation layers at the outside of your domain that transform incoming and outgoing data into/out of domain models.</p>
<p>So the data mesh connects small fiefdoms, each owning and shaping the data relevant to their domain.</p>

<pre class="mermaid">
graph LR
    subgraph "Billing Domain"
        BC[Billing Customer<br/>- id<br/>- name<br/>- vat_number<br/>- payment_terms]
        BillingApp[Billing Service]
        BillingApp <--> BC
    end

    subgraph "Sales Domain"
        SC[Sales Customer<br/>- id<br/>- name<br/>- credit_limit<br/>- account_value]
        SalesApp[Sales Service]
        SalesApp <--> SC
    end

    subgraph "Support Domain"
        SuC[Support Customer<br/>- id<br/>- name<br/>- support_tier<br/>- ticket_count]
        SupportApp[Support Service]
        SupportApp <--> SuC
    end

    BC <-.->|Anti-Corruption<br/>Layer| SC
    SC <-.->|Anti-Corruption<br/>Layer| SuC
    SuC <-.->|Anti-Corruption<br/>Layer| BC
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><p>It&rsquo;s a very flexible design but while Data Mesh solves the coupling problem, it introduces a new set of challenges.</p>
<p>If I’m an analyst trying to find &lsquo;Customer Revenue,&rsquo; do I look in Sales, Billing, or Marketing? The answer is usually &lsquo;all of the above.&rsquo; In a pure Mesh, you don&rsquo;t make multiple calls, you have to build multiple Anti-Corruption Layers just to get a simple report. It requires a high level of architectural maturity and that is something not every low-code or legacy team possesses.</p>
<p>You also need a very capable team, not only from a technical point of view, but also from an architectural point of view. All of your teams need to be very aware, and willing, to fully commit to this setup. That includes low-code and legacy teams.</p>
<p>And what about SaaS and 3rd party? Sure, you can abstract that. Something you also have to do with the CDM, but it&rsquo;s again more infrastructure.</p>
<p>I&rsquo;m also not sure about the data and analytics teams. They now have a ton of overhead.</p>
<h2 id="federated-hub-and-spoke-data-strategy">Federated Hub-and-Spoke Data Strategy</h2>
<p>Let&rsquo;s try and see if we can combine these two strategies. On one hand we want a clear data setup that is discoverable, reportable, and has easy integration. On the other hand we don&rsquo;t want the huge model overhead, slow design committee, and data quality.</p>
<p>Big enterprises have Data lakes/Data lake houses. They are mainly hosted in tools like MS Fabric, Databricks, Snowflake, … Data scientists love these tools. These days these tools come battery included. They have out of the box APIs, transformers, notebooks, serverless code, &hellip;</p>
<p>What if we leverage that setup? We centralize our data in a central lake. Yes, that is back to the CDM setup. But we split it up in federated domains. You have a base Customer table that you call CustomerIdentity that is connected to a SalesCustomer, SupportCustomer, &hellip; Think of this as logical inheritance, a &lsquo;CustomerIdentity&rsquo; record that is extended by domain-specific tables through a shared primary key.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-SQL" data-lang="SQL"><span class="line"><span class="cl"><span class="c1">-- Central Lake
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">CustomerIdentity</span><span class="w"> </span><span class="err">{</span><span class="w"> </span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">email</span><span class="p">,</span><span class="w"> </span><span class="n">createdAt</span><span class="p">,</span><span class="w"> </span><span class="n">updatedAt</span><span class="p">,</span><span class="w"> </span><span class="n">createdBy</span><span class="w"> </span><span class="err">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">CustomerSalesDomain</span><span class="w"> </span><span class="err">{</span><span class="w"> </span><span class="n">customerId</span><span class="p">,</span><span class="w"> </span><span class="n">accountValue</span><span class="p">,</span><span class="w"> </span><span class="n">salesRep</span><span class="p">,</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="err">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">CustomerSupportDomain</span><span class="w"> </span><span class="err">{</span><span class="w"> </span><span class="n">customerId</span><span class="p">,</span><span class="w"> </span><span class="n">supportTier</span><span class="p">,</span><span class="w"> </span><span class="n">lifetimeTickets</span><span class="p">,</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="err">}</span><span class="w">
</span></span></span></code></pre></div><p>When you create a new Customer in your sales tool you trigger an event. The CustomerCreate event<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>. The CustomerCreate trigger fills out the base information for the Customer (username, firstName, lastName) in the central data lake, at the same time we store our customer (base and domain specific data) in our local database. You also do this for delete and update events. The base information goes to the server, the domain specific data stays on the sales tool as a single source of truth.</p>
<p>The CustomerCreate event (or update/delete) also triggers all the other tools that are subscribed to that event. That being tools with Customers.</p>
<p>Every night there is a sync of the domain tools to the central lake to fill out the domain tables with a delta. Please note that the Lake is not the single source of truth here. It might be for the CustomerIdentity, but not for the domain data.</p>

<pre class="mermaid">
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#2d5016','primaryTextColor':'#000','primaryBorderColor':'#1a2e0a','lineColor':'#2d5016','secondaryColor':'#4a7c2c','tertiaryColor':'#fff'}}}%%
sequenceDiagram
    participant Sales as Sales Service
    participant Lake as Central Data Lake
    participant Support as Support Service
    participant Billing as Billing Service

    Note over Sales,Billing: Real-time Customer Creation
    Sales->>Sales: Create Customer locally
    Sales->>Lake: CustomerCreate Event<br/>(id, name, email)
    activate Lake
    Lake->>Lake: Store in CustomerIdentity
    Lake-->>Support: Broadcast CustomerCreate
    Lake-->>Billing: Broadcast CustomerCreate
    deactivate Lake

    Support->>Support: Create local Customer record<br/>(with base data)
    Billing->>Billing: Create local Customer record<br/>(with base data)

    Note over Sales,Billing: Nightly Domain Data Sync
    Sales->>Lake: Sync domain data<br/>(accountValue, salesRep, etc)
    Support->>Lake: Sync domain data<br/>(supportTier, tickets, etc)
    Billing->>Lake: Sync domain data<br/>(vat_number, terms, etc)
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script>
<pre class="mermaid">
graph TB
    subgraph "Central Data Lake"
        CI[CustomerIdentity<br/>- id<br/>- name<br/>- email<br/>- createdAt<br/>- updatedAt]

        CSD[CustomerSalesDomain<br/>- customerId<br/>- accountValue<br/>- salesRep]

        CSuD[CustomerSupportDomain<br/>- customerId<br/>- supportTier<br/>- lifetimeTickets]

        CBD[CustomerBillingDomain<br/>- customerId<br/>- vat_number<br/>- payment_terms]

        CI <-.->|extends| CSD
        CI <-.->|extends| CSuD
        CI <-.->|extends| CBD
    end

    subgraph "Sales Domain"
        SalesApp[Sales Service]
        SalesDB[(Sales DB)]
        SalesApp <--> SalesDB
    end

    subgraph "Support Domain"
        SupportApp[Support Service]
        SupportDB[(Support DB)]
        SupportApp <--> SupportDB
    end

    subgraph "Billing Domain"
        BillingApp[Billing Service]
        BillingDB[(Billing DB)]
        BillingApp <--> BillingDB
    end

    SalesApp <-->|Real-time Events<br/>Create/Update/Delete| CI
    SupportApp <-->|Real-time Events<br/>Create/Update/Delete| CI
    BillingApp <-->|Real-time Events<br/>Create/Update/Delete| CI

    SalesDB <-.->|Nightly Sync<br/>Domain Data| CSD
    SupportDB <-.->|Nightly Sync<br/>Domain Data| CSuD
    BillingDB <-.->|Nightly Sync<br/>Domain Data| CBD
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><p>Now if we have a call from the sales tool to the Support tool, you only send over the base customer data<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>. And the Support tool enriches that base data with their own specific domain data.</p>
<p>We don&rsquo;t do this for only the Customer data. You&rsquo;ll want to do this for all shared data.</p>
<h2 id="consuming-data-without-breaking-domains">Consuming data without breaking domains</h2>
<p>This is not a simple setup, I&rsquo;m very aware of that. I would not propose it if I didn&rsquo;t see some big pro&rsquo;s.</p>
<h3 id="upsides">Upsides</h3>
<p>First up is that you have a central data record that is at most a day old. That sounds a lot in development terms, but is very doable from a data and analytics point of view. If you really need to, you can always tweak the events.</p>
<p>Governance tooling (Purview, Atlan) works well with centralized lakes. Data retention, GDPR, data sensitivity are big things in enterprises. We can all fully utilize these and sync them downstream.</p>
<p>The domain owns the domain data. We support the bounded context approach while still making the data discoverable and traceable outside the IT department.</p>
<p>This supports Legacy, SaaS, Serverless, and Low Code applications. You will not hook them up to the event chain, but you can connect to the central data lake. They almost always support GraphQL. I&rsquo;m personally not a fan of GraphQL, but I do see a good case here.</p>
<p>The payloads are very controllable. We don&rsquo;t send over these massive objects. But we are still able to fully migrate the data from the central place.</p>
<p>We have separation of concerns. Our domains focus on transactions (OLTP) and our lake focuses on analytics (OLAP).</p>
<h3 id="downsides">Downsides</h3>
<p>That CustomerIdentity base table needs to be very lean. You might run back into discussions about adding fields. This will require discipline, but it&rsquo;s way easier compared to the CDM version. Domains can duplicate common fields (FAX in Sales AND Shipping). Not an issue, storage is cheap.</p>
<p>You will have to avoid the best category of bugs: racing conditions. Yes that&rsquo;s an issue. We have been writing and dealing with them since the first computer systems. Stick to good practices, and it&rsquo;s not the death sentence everyone always claims it is. <sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.</p>
<p>These nightly syncs need to be thought out, especially if you are in multiple (all?) timezones. This is something you can fix with queues. You&rsquo;re using them anyway for your events.</p>
<h2 id="data-architecture-is-very-personal">Data architecture is very personal</h2>
<p>Please don&rsquo;t go out there and implement this like I&rsquo;ve written it out. This is not a one-size-fits-all approach. This post is more to give you an insight in a possible architecture.</p>
<p>There is so much more to really dig in into this approach. But for now it can just be a nice thought experiment and maybe part of this can be somewhere in your toolbox for when you might ever need it.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>If you&rsquo;re into DDD you might have a lot of opinions at this point, no worries, we&rsquo;ll get there.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>You also want an address, but that will greatly muddy the waters for this example&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Very important here is to use all the best practices of events. Most important is datastamps, Idempotent event handlers, and rollbacks&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>You could only send over the customerID, but then you have an extra call each time&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>The death of the enterprise service bus was greatly exaggerated</title><link>https://frederickvanbrabant.com/blog/2026-01-22-the-dead-of-the-enterprise-service-bus-was-greatly-exaggerated/</link><pubDate>Thu, 22 Jan 2026 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2026-01-22-the-dead-of-the-enterprise-service-bus-was-greatly-exaggerated/</guid><description>&lt;p&gt;Every six months or so I read a post on sites like Hackernews that the enterprise service bus concept is dead and that it was a horrible concept to begin with. Yet I personally have great experiences with them, even in large, messy enterprise landscapes.&lt;/p&gt;
&lt;p&gt;I would go even further and say that I see a resurgence in their usage in big enterprises. Even companies that moved away from them seem to have a renewed appetite. Sometimes they are rebranded as integration platforms / iPaaS, but they are still very much the thing under the hood.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>Every six months or so I read a post on sites like Hackernews that the enterprise service bus concept is dead and that it was a horrible concept to begin with. Yet I personally have great experiences with them, even in large, messy enterprise landscapes.</p>
<p>I would go even further and say that I see a resurgence in their usage in big enterprises. Even companies that moved away from them seem to have a renewed appetite. Sometimes they are rebranded as integration platforms / iPaaS, but they are still very much the thing under the hood.</p>
<p>This seems like the perfect opportunity to write an article about what they are, how to use them and what the pitfalls are. From an enterprise architecture point of view that is, I&rsquo;ll leave the integration architecture to others.</p>
<p>Enterprise service buses can do very technical things, but that is not what I want to focus on today. I know there are real upsides (and downsides) to the translation layers in these tools (for example), but today we want to focus on what the business value is of using an ESB.</p>
<h2 id="what-is-an-esb">What is an ESB</h2>
<p>When people ask me what an enterprise service bus (ESB) is, I like to use an airport metaphor.</p>
<p>You can see an ESB as an airport hub, specifically one for connecting flights. An airplane comes in, drops their passengers, they sometimes have to pass security, and they go on another flight to their final destination.</p>
<p>Most people like to have a direct flight, but from an organizational point of view it makes sense to have a lay-over. You can bring people from New-York to London, and then plan flights from London to places in Europe. Not everyone from the original flight in New-York wants to go to Milan, but other people from other flights might want to.</p>
<p>So what does that all mean in a technical sense?</p>
<p>An ESB is a mediation layer that can do routing, transformation, orchestration, and queuing. And, more importantly, centralizes responsibility for these concerns.</p>
<p>In a very basic sense you connect application A to one end of the ESB, and application B &amp; C the other. And you only have to worry about those connections from and to the ESB.</p>

<pre class="mermaid">
graph LR
AA[Application A] --> ESB
ESB --> AB[Application B]
ESB --> AC[Application C]
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><h2 id="the-big-upsides-for-the-organization">The big upsides for the organization</h2>
<p>With the basics out of the way, let&rsquo;s focus on these other aspects. But let&rsquo;s focus on them from an organizational point of view.</p>
<h3 id="decoupling-at-the-edges">Decoupling at the edges</h3>
<p>Say you want to replace an invoicing tool. This is probably not a small operation. You will have to see what processes run in this tool, what data supports these processes, and how to get them. Once you have all that, you will have to make a gap analysis versus the new tool. This will include work on all the applications that feed the invoicing tool this data.</p>

<pre class="mermaid">
graph LR
AA[Timesheet tool] -->|Timesheets| InvoiceTool[Invoicing tool]
Ab[Employee Database] -->|Employee Information| InvoiceTool[Invoicing tool]
AC[CRM] -->|Client Information| InvoiceTool[Invoicing tool]
AD[SAP] -->|Project Information| InvoiceTool[Invoicing tool]
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><p>Now if you were to have an ESB in place for this, you would only have to focus on the ESB to Invoicing tool part.</p>

<pre class="mermaid">
graph LR
AA[Timesheet tool] -->|Timesheets| ESB
Ab[Employee Database] -->|Employee Information| ESB
AC[CRM] -->|Client Information| ESB
AD[SAP] -->|Project Information| ESB
ESB --> |Invoicing Information| InvoiceTool[Invoicing tool]
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><p>Next we have to focus on what the new invoicing tool needs in terms of information. If it&rsquo;s the same, but with different field names, that is all handled in the ESB, and we would not have to touch the other applications. This drastically reduces the blast radius of change.</p>
<p>If we require extra information, we would still have to touch the connections going to the ESB, but it would only be to add fields. We don&rsquo;t have to worry about REST vs SOAP vs FTP stuff. As that won&rsquo;t really impact downstream.</p>
<h3 id="centralized-integration-control">Centralized integration control</h3>
<p>An ESB can also give you more control over these connections. Say your ordering tool suddenly gets hammered by a big sale. The website might keep up, but your legacy orders tool might not.</p>
<p>Situations like this break down at the weakest link. You can work around that with smart architecture, but you don&rsquo;t always have control over that with SaaS or legacy tooling.</p>
<p>Here again with an ESB in the middle you can queue these calls. Say everything keeps up, but the legacy mail system can&rsquo;t handle the load. No problem, we keep the connections in a queue, they are not lost, and we throttle them. Instead of a fire hose of non-stop requests, the tool now gets 1 request a second.</p>
<p>That is not ideal from a product point of view, people have to maybe wait 10 minutes for their order confirmation, but the sales went through. The people will get their product they bought, and you don&rsquo;t have the awkward conversation with the leadership about how we lost a bunch of money from lost sales from the architecture that wasn&rsquo;t up to par.</p>
<p>The focus is on absorbing variability at the platform level instead of the application level.</p>
<h3 id="operational-visibility">Operational visibility</h3>
<p>If, in theory, all connections go over the ESB <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> you can also keep an eye on all information that flows through it. Especially for an enterprise architect&rsquo;s office that&rsquo;s a very nice thing.</p>
<p>You can have with little effort an overview of everything that is connected and what data flows over it. You integrations teams will now have deep knowledge about all the connections, something that normally is spread out over different teams and tools.</p>
<p>Also from a security point of view this is a big win. You have an overview of all connections and what they all use as standards, a perfect starting (and tracking) point to modernize them. Without messing up all the other connections.</p>
<h2 id="but-that-is-all-in-theory">But that is all in theory</h2>
<p>All of those upsides are very true. But that&rsquo;s only the case if you go all in and are very strict in how to use them. And that&rsquo;s where a lot of the bad experiences come up.</p>
<h3 id="hidden-business-logic">Hidden business logic</h3>
<p>Take for example that translation layer. Your source has a <code>firstName</code> and a <code>lastName</code> field, but your sink only accepts a <code>Name</code> field. OK, no problem <code>name = concat(firstName, ' ', lastName)</code>. No harm done.</p>
<p>But then for American users it needs to be with middle names. So we might get an <code>if/else</code> in there. And then for Asian users it&rsquo;s last name and then first name. That <code>if/else</code> is now a <code>switch</code>.</p>
<p>Before you know it you are writing business critical logic in a text-box of an integration layer. No testing, no documentation, no source control …</p>
<p>In reality, you’ve now created a shadow domain model inside the ESB.</p>
<p>This is often the core of all those “ESBs are dead” posts.</p>
<h3 id="tight-coupling-disguised-as-loose-coupling">Tight coupling disguised as loose coupling</h3>
<p>Yes you can plug and play connections, but everything is still concentrated in the ESB. That means that if the ESB is slow, everything is slow. And that is nothing compared to the scenario where it&rsquo;s down.</p>
<p>You are also putting all your eggs in the same basket for vendor lock in. If you have a few hundred applications connected to this central platform, what are you going to do when they hike the price 50%? Decoupling all of these applications away from this central layer is going to be a momentous undertaking.</p>
<p>You could always build one yourself, but that is again a team that needs to exist and building an ESB from scratch is a huge undertaking<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. Not something I would recommend for non-tech companies.</p>
<p>If you are not explicitly willing to run an integration platform as a product, you probably shouldn’t build one.</p>
<h3 id="skill-bottlenecks">Skill bottlenecks</h3>
<p>You can always train people into ESB software, and it&rsquo;s not necessarily the most complex material in the world (depends on how you use it), but it is a different role. One that you are going to have to go to the market for to fill. At least when you are starting to set it up, you don&rsquo;t want someone who&rsquo;s never done it to “give it a try” with the core nervous system of your application portfolio.</p>
<p>If you compare it to point-to-point connections, those are done by software developers. Be it Java, .net, JavaScript,&hellip; profiles. You already have those, and they can jump in from another team if there is a time crunch<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.</p>
<h3 id="cost">Cost</h3>
<p>It goes without saying that you pay for this service. Some companies charge per connection, others per data usage. There are different cost strategies.</p>
<p>This is an extra cost you would not have when you do point-to-point. The promise is naturally that you retrieve that cost by having simpler projects and integrations. But that is something you will have to calculate for the organization.</p>
<h2 id="modelling-an-enterprise-service-bus">Modelling an enterprise service bus</h2>
<p>Now how do you include this in our meta-model. This is actually also a discussion point. Is this an application or a platform or a system? My suggestion is (like in the case of Entra/active directory), map it to your connections. Let me explain.</p>

<pre class="mermaid">
graph LR

Application1 & Application2 & Application3 <--> ESB
ESB <--> Application4 & Application5 & Application6
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><p>If you map your ESB as an application you will quickly run into this diagram. Everything is connected to the ESB, and every connection goes out of the ESB. It&rsquo;s impossible to track what actually goes where.</p>
<p>You could argue that that&rsquo;s not an issue, as you can indeed connect everything in the ESB and see it as a data source. But the value of your diagramming will be useless then.</p>
<p>ESBs collapse many architectural concerns into a single box, which breaks most modelling conventions.</p>
<p>What I like to do is (and most EA tooling allows that) is to add parameters to the connections. One of these parameters is the type of connection<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>. I then colour code these connections. For example, all orange connections are ESB connections.</p>

<pre class="mermaid">
graph LR

Application1 <==> Application3
Application2 ==> Application1
Application2 -.-> Application4
Application2 -.-> Application5
Application5 <-.-> Application6

linkStyle 2 stroke:#ff6600,stroke-width:2px
linkStyle 3 stroke:#ff6600,stroke-width:2px
linkStyle 4 stroke:#ff6600,stroke-width:2px
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><p>But that all depends on the type of diagram you want to create.</p>
<h2 id="when-to-use-an-esb">When to use an ESB</h2>
<p>Enterprise service buses only make sense in big organizations (hence the name). But even there is no guarantee that they will always fit.</p>
<p>I would only really advise using them if your application portfolio is relatively large and is changing often. They also mainly shine when there is a mixture of SaaS, legacy, and more modern in house applications.</p>
<p>If your portfolio is full of homemade custom applications I would maybe skip this setup. You have the developers, use the flexibility you have.</p>
<p>There is also the funding aspect; these setups add another cost that is not always clear at the beginning for most organizations (if you pay per MB, it can be really hard to grasp how much GB you are shipping over these connections).</p>
<p>So yes, I&rsquo;m sure I&rsquo;ll read another post on Reddit in a few months about how enterprise service buses are a thing everyone should avoid, and everyone is going to be sharing their horror stories in the comment section (some of these are pretty good). But I&rsquo;m sure I&rsquo;ll be seeing ESBs for years to come at all big organizations. Even if it has a new name or logo this time around.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>You don&rsquo;t want that in practice. There is latency, cost and just overhead involved. More on that in the Downside section&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>If you want to tackle this, make sure to have a clear outline on how to build it. I would recommend looking into:</p>
<ul>
<li>Pipes and Filters Pattern</li>
<li>Strategy Pattern</li>
<li>Provider Pattern</li>
<li>Registry Pattern</li>
</ul>
&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></li>
<li id="fn:3">
<p>compared to having to learn an entire new role&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>I also do this for FTP, REST, SOAP,&hellip;&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Bring back opinionated architecture</title><link>https://frederickvanbrabant.com/blog/2026-01-09-bring-back-opinionated-architecture/</link><pubDate>Fri, 09 Jan 2026 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2026-01-09-bring-back-opinionated-architecture/</guid><description>&lt;p&gt;Before my end-of-year holiday break, I received an email &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; from someone who read my post about “&lt;a href="https://frederickvanbrabant.com/blog/2025-11-28-choosing-your-starting-line-in-enterprise-architecture/"
target="_blank"
&gt;Choosing your starting line in enterprise architecture&lt;/a&gt;”. Mark asked me what I meant by the line:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So yes, you can map the AS-IS. You can design the TO-BE. You can even claim you’re doing both, which is the classic architect escape hatch.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>Before my end-of-year holiday break, I received an email <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> from someone who read my post about “<a href="https://frederickvanbrabant.com/blog/2025-11-28-choosing-your-starting-line-in-enterprise-architecture/"




 target="_blank"
 


>Choosing your starting line in enterprise architecture</a>”. Mark asked me what I meant by the line:</p>
<blockquote>
<p>So yes, you can map the AS-IS. You can design the TO-BE. You can even claim you’re doing both, which is the classic architect escape hatch.</p></blockquote>
<p>And I had to be honest with him, it was mainly a throwaway joke poking fun at the fact that most architects when they get a question, the first thing they will answer is “it depends”.</p>
<p>But that mail exchange stuck a bit in my mind over the holiday break. Enterprise architecture claims to bring clarity, but often hides behind ambiguity. And maybe that’s something we need to confront.</p>
<h2 id="borrowing-yagni-from-the-developer-world">Borrowing YAGNI from the developer world</h2>
<p>When I was a developer<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, I was always attracted to highly opinionated libraries and frameworks. I always preferred a single way of doing things, over three different ways to do it, and they all have their pros and cons.</p>
<p>Even if that way of doing it was not how I would personally do it. I know that the person that made the library or framework thought about it deeply, came to the conclusion “this is best”, and fully supports and documents that single way.</p>
<p>It just makes the framework or library easier to talk about and understand.</p>
<p>This is part of the YAGNI (You ain&rsquo;t gonna need it)<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> mindset. Don&rsquo;t start covering and building scenarios that might or might not actually happen in the future.</p>
<p>Translating the same ideas to enterprise architecture would be the typical preparations that we build into systems for future strategy. Building a new application has connections for data in the form of a REST API, but also building an optional sFTP part in case that you have to connect some legacy tool to it in the future.</p>

<pre class="mermaid">
flowchart TB
    subgraph Opinionated
        direction TB
        Application[ERP] ===> intA((REST Interface))
        Application[ERP] -.-> intE((Potential future sFTP Interface))
        intA ==> AppA[New Application]
        intE -.-> AppB
        AppB[Old application that we might connect in the future]
    end

    subgraph Unopinionated["Just in case architecture"]
        direction TB
        ApplicationB[ERP] ===> intB((REST Interface)) & intC((sFTP Interface)) & intD((GraphQL Interface))
        intB ==> AppC[New Application]
        intC -.-> AppD
        AppD[Old application that we might connect in the future]
    end
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><p>That is very unopinionated software. How do you connect to this application? Ah, here are the options.</p>
<p>That sounds nice from a service point of view. But if everything is internal, that service that you provide to other applications is going to cost a lot of money in development and maintenance of these options. That lack of strategy at this level will also make your entire application landscape less coherent, as there is no unified way of working.</p>
<h2 id="it-depends-as-architectural-debt">“It depends” as architectural debt</h2>
<p>Don&rsquo;t get me wrong, I&rsquo;m not advocating here for abandoning backup plans and putting all your eggs in one basket. What I am advocating for is architectural courage.</p>
<p>Are all these “it depends” and “future-proofing” mantras there to get to a more correct solution, or just there to minimize your personal responsibility if it all goes haywire?</p>
<p>If it&rsquo;s the latter, you have to ask yourself what the value of that architecture really is. If the business leans on your expertise to set up a project and for every decision you have you just cover all options, there is no real point in your expertise. You are just another meeting and checkbox in an approval process.</p>
<p>You also have to calculate the cost of it all. In the above senario where you cover all your bases and build a REST API and an sFTP connection because &ldquo;you might need it in the future&rdquo;, you will have to maintain, secure, document, train and test both. For years to come. Just another think that can break.</p>
<p>That would be ok if that senario actually plays out. If the company strategy changes, and the company never connects the two applications, all of that has been for nothing.</p>
<p>In the case of not optimizing ahead of time for that senario (but still keeping it in the back of your mind). You can just implement it when it&rsquo;s actually needed. There will be a lower cost, the company will have appetite for the change, and you might have a clearer view of the actual data that will flow over the connection.</p>

<pre class="mermaid">
xychart
    title "Cumulative Cost: Upfront Optionality vs. Adapting when needed"
    x-axis "Time (Years)" [Y1, Y2, Y3, Y4, Y5]
    y-axis "Cumulative Effort/Cost" 0 --> 100
    line [20, 40, 60, 80, 100]
    line [10, 20, 30, 50, 70]
</pre>


<script type="module">
  import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/+esm";

  
  const FRONTMATTER_REGEX = /---\s*\n(?:.*\n)*?---/;
  const CONFIG_REGEX = /(---\s*\n(?:.*\n)*?config:\s*\n)((?:.*\n)*?)(---)/m;
  const THEME_REGEX = /^\s*theme:/;
  const THEME_VARS_REGEX = /^\s*themeVariables:/;

  
  const getMermaidConfig = (startOnLoad = true) => ({
    startOnLoad,
    theme: "default",
    securityLevel: "loose",
    flowchart: { curve: "basis" },
  });

  
  function processContent(content, isDarkMode) {
    if (!isDarkMode) return content;

    const hasFrontmatter = FRONTMATTER_REGEX.test(content);

    if (hasFrontmatter) {
      return content.replace(CONFIG_REGEX, (match, before, configContent, after) => {
        const lines = configContent.split("\n");
        const filtered = [];
        let skipUntilDedent = false;
        let themeVarIndent = 0;

        for (const line of lines) {
          const indent = line.search(/\S/);

          if (THEME_REGEX.test(line)) continue;

          if (THEME_VARS_REGEX.test(line)) {
            skipUntilDedent = true;
            themeVarIndent = indent;
            continue;
          }

          if (skipUntilDedent) {
            if (line.trim() && indent > themeVarIndent) continue;
            skipUntilDedent = false;
          }

          filtered.push(line);
        }

        filtered.unshift("  theme: dark");
        return before + filtered.join("\n") + after;
      });
    }
    
    return `---\nconfig:\n  theme: dark\n---\n${content}`;
  }

  
  async function rerenderDiagrams() {
    const isDark = document.documentElement.classList.contains("dark");
    const elements = document.querySelectorAll(".mermaid");
    
    if (elements.length === 0) return;

    const validElements = [];

    for (const element of elements) {
      if (!element.isConnected) continue;
      
      const originalContent = element.getAttribute("data-original");
      if (originalContent) {
        const content = processContent(originalContent, isDark);
        element.textContent = content;
        element.removeAttribute("data-processed");
        validElements.push(element);
      }
    }

    if (validElements.length === 0) return;

    await new Promise(resolve => setTimeout(resolve, 0));

    try {
      await mermaid.run({ nodes: validElements });
    } catch (error) {
      console.error("Mermaid rendering error:", error);
    }
  }

  
  document.addEventListener("DOMContentLoaded", () => {
    const isDark = document.documentElement.classList.contains("dark");

    document.querySelectorAll(".mermaid").forEach((element) => {
      if (!element.hasAttribute("data-original")) {
        const originalContent = element.textContent.trim();
        element.setAttribute("data-original", originalContent);
        element.textContent = processContent(originalContent, isDark);
      }
    });
  });

  mermaid.initialize(getMermaidConfig());

  
  let previousDarkMode = document.documentElement.classList.contains("dark");

  new MutationObserver(() => {
    const currentDarkMode = document.documentElement.classList.contains("dark");

    if (currentDarkMode !== previousDarkMode) {
      previousDarkMode = currentDarkMode;
      mermaid.initialize(getMermaidConfig(false));
      rerenderDiagrams();
    }
  }).observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["class"],
  });
</script><h2 id="the-myth-of-easy-swapping">The myth of “easy swapping”</h2>
<p>Then there is the conversation of the easy-off ramp in implementing new software.</p>
<p>When implementing new applications, you already prepare for ways to off-board the application by making your systems hot-swappable or technologically agnostic. Even going as far as having applications that can use whatever data-provider or environment.</p>
<p>A lot of architects will tell you that&rsquo;s an essential part of architecture.</p>
<p>You can do these things with enterprise-service-buses and interfaces or gateways, maybe even front-loaded microservice middleware. There are more technological options than you can imagine, and all of these are great conference talks.</p>
<p>The problem is, like in all nifty architectural setups, the actual users. It&rsquo;s cool that you can hot swap your incoming data from one service to a different one in less than a week! Now we just need six months of new training, new processes, new KPIs, new goal setting and hiring to use said new data source.</p>
<p>Technology can be made swappable, but organizations almost never are.</p>
<p>Besides that, how often have you actually ever swapped a database in a system? I know ORMs are great, but the entire claim that you can swap back and forward from MySQL to MongoDB has always been a moot point to me. You build applications with a certain flavour of data storage in mind, why would you suddenly want to change over to a total different flavour?</p>
<p>Please remember that you can always change setups in the future. That is part of the entire agile way of working that companies like to talk about.</p>
<h2 id="choosing-a-side">Choosing a side</h2>
<p>I’m not suggesting we should all become architectural &ldquo;dictators&rdquo; who refuse to listen to edge cases. But I am suggesting that we stop being so deep into &ldquo;what-if&rdquo; and start focusing more on &ldquo;what-is.&rdquo;</p>
<p>The next time you’re sitting in a design session and that familiar urge to build a &ldquo;just-in-case&rdquo; abstraction layer hits you, pause. Ask yourself: am I building this because the business needs it, or because I’m afraid of being wrong?</p>
<p>Being opinionated doesn’t mean being rigid, it&rsquo;s more about actually having a plan. It means having the courage to say, &ldquo;This is the path we are taking because it is the most efficient one for today.&rdquo; If the strategy changes in two years, you deal with it then, with the benefit of two years of lower maintenance costs and a leaner system.</p>
<p>And the thing we often forget is that the business hates all of these &ldquo;what if&rdquo; senario&rsquo;s, they just don&rsquo;t care about it. They mainly want it to work. If your &ldquo;opinionated&rdquo; choice needs to change later, they won&rsquo;t remember the technical specifics of why you didn&rsquo;t build three different API types. They’ll remember that you got the project live on time and it worked when they needed it.</p>
<p>So, let’s stop taking the escape hatch of &ldquo;it depends.&rdquo; Pick a lane, document it well, and build it simply. I promise, the sky won’t fall and your application landscape (and your sanity) will thank you for it.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I have a contact page. This is the first mail I&rsquo;ve ever got from that contact page. If you would like to know more or have comments on something I write, you&rsquo;re always welcome to drop me a message.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>There days I only code as a hobby.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>YAGNI goes further than that, I know&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>What is a Value Stream and how does it relate to a Value Chain</title><link>https://frederickvanbrabant.com/blog/2025-12-12-what-is-a-value-stream-and-how-does-it-relate-to-a-value-chain-copy/</link><pubDate>Sat, 13 Dec 2025 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2025-12-12-what-is-a-value-stream-and-how-does-it-relate-to-a-value-chain-copy/</guid><description>&lt;p&gt;Organizations often use “value stream” and “value chain” as interchangeable labels. It&amp;rsquo;s not the biggest architectural drama in the world, but it&amp;rsquo;s still something that always annoys me a little.&lt;/p&gt;
&lt;p&gt;We as architects might actually be to blame for this. We keep on coming up with related concepts that are very close to each other and then naming them ever so slightly differently.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>Organizations often use “value stream” and “value chain” as interchangeable labels. It&rsquo;s not the biggest architectural drama in the world, but it&rsquo;s still something that always annoys me a little.</p>
<p>We as architects might actually be to blame for this. We keep on coming up with related concepts that are very close to each other and then naming them ever so slightly differently.</p>
<p>I think this could be a fun pair of concepts to dive into as it touches some deeper architectural ideas. So let&rsquo;s learn when you stream and when you chain.</p>
<h2 id="what-is-value">What is “Value”</h2>
<p>We cannot start talking about Value Chains and Value Streams before we actually know what we are going to chain or stream. In the context of TOGAF and ArchiMate, “Value” is not just money; it is the benefit a person or a group of people get.</p>
<p>This can take many forms. It can be financial; selling a product for money. But it can also be producing that product (the value is having a product) or providing a service (hiring a person).</p>
<p>If we are talking about value, the “who” is critical. This can be a person or a group of people. You cannot have value if nobody actually finds it valuable<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>In the case of hiring a person, you have multiple people that get value: The team that now has more people to handle the workload, but also the person that is hired as they now have a job.</p>
<h2 id="and-how-do-you-stream-said-value">And how do you stream said value?</h2>
<p>To achieve value, we break the path toward the outcome into stages. At each stage we add (ideally) incremental value, where the final stage should bring the promised value.</p>
<p>


  
  <figure class="my-10">
    <picture>
      <source srcset="/images/posts/2025-12-12-What-is-a-Value-Stream-and-how-does-it-relate-to-a-Value-Chain/value-stream_hu_4ef8c44fcf1afeff.webp" type="image/webp" />
      <img
        src="/images/posts/2025-12-12-What-is-a-Value-Stream-and-how-does-it-relate-to-a-Value-Chain/value-stream.png"
        alt="A schema of a value stream"
        width="683"
        height="113"
        loading="lazy"
        class="mx-auto shadow-sm border border-gray-100" />
    </picture>
  </figure>

</p>
<p>If we map out what we actually do in organization, we can measure it. We can add SLAs <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> to the stages of the value stream. This ensures a standardized quality outcome. We
formalize the way we work in an organization.</p>
<p>This might feel like a business process, and there are indeed overlapping ideas. A value stream is more the rough outlines. It is however a good idea to link your business processes to these parts of the value stream. So you can start tracing them down towards processes, software and roles. <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<p>The nice thing about value streams is that it slices through the organization. Something a business process is too specialized to fully do.</p>
<p>To onboard a new customer, you lean on different business units working together to actually fully onboard the new customer. You will need the legal team to help with the contract, a project team to set up the project, a finance team to set up the billing&hellip;</p>
<p>If you see that your onboarding of customers value stream doesn&rsquo;t work that well, you don&rsquo;t go blame the legal team because they take too long to go over the documents. You look at the entire stream to see where there are bottlenecks.</p>
<p>This is how you often discover issues like missing or late information to the right teams.</p>
<h2 id="then-what-is-a-value-chain">Then what is a Value Chain?</h2>
<p>Enter <a href="https://en.wikipedia.org/wiki/Michael_Porter"




 target="_blank"
 


>Michael Porter</a> and his book <em>Competitive Advantage</em>, where he coined the concept of the Value Chain.</p>
<p>In the book, Porter takes the organization as a collection of distinct activities that, when combined, create profit. We then take these activities and group them into “Primary Activities” (like Inbound Logistics, Operations, Sales) and “Support Activities” (like HR, Technology).</p>
<p>This mean that you have units that create direct customer value, and units that support the primary activities in creating value. These support activities create just as much value, but they have a different client: the primary activities.</p>
<p>By mapping an organization out like this, you can take a deeper look at the units themselves and how they work and create value. All at a deeper detail compared to the overarching organization that has a too broad overview.</p>
<p>The total outcome of these groups is a Value Chain. So every organization has one Value Chain. Some organizations use sub-value chains, where every activity has their own value chain, but then we are getting dangerously close to Values Streams again.</p>
<p>


  
  <figure class="my-10">
    <picture>
      <source srcset="/images/posts/2025-12-12-What-is-a-Value-Stream-and-how-does-it-relate-to-a-Value-Chain/value-chain_hu_799f0b2a4c8099e5.webp" type="image/webp" />
      <img
        src="/images/posts/2025-12-12-What-is-a-Value-Stream-and-how-does-it-relate-to-a-Value-Chain/value-chain.png"
        alt="A schema of a value chain"
        width="1408"
        height="768"
        loading="lazy"
        class="mx-auto shadow-sm border border-gray-100" />
    </picture>
  </figure>

</p>
<h2 id="but-arent-value-streams-and-a-value-chains-the-polar-opposites-then">But aren&rsquo;t Value Streams and a Value Chains the polar opposites then?</h2>
<p>You might have noticed that a value stream brings a view of an organization working together to create value, where a value chain is creating silo&rsquo;s and breaking an organzation apart.</p>
<p>That&rsquo;s correct, but they solve different functions and contexts.</p>
<p>Porter’s Value Chain describes how the organization thinks about its strategic functions. They focus on the work inside each business unit and how it can be organized and optimized. Very siloed, and vertical.</p>
<p>Value Streams, on the other hand, describe how the organization actually delivers outcomes across those business units. They follow the processes for start to finish.</p>
<p>They are perpendicular viewpoints. Different lenses of looking at the work.</p>
<p>


  
  <figure class="my-10">
    <picture>
      <source srcset="/images/posts/2025-12-12-What-is-a-Value-Stream-and-how-does-it-relate-to-a-Value-Chain/stream-vs-chain_hu_e95c42341aad6a1c.webp" type="image/webp" />
      <img
        src="/images/posts/2025-12-12-What-is-a-Value-Stream-and-how-does-it-relate-to-a-Value-Chain/stream-vs-chain.png"
        alt="A schema that compares a vertical chain to a horizontal stream"
        width="1408"
        height="768"
        loading="lazy"
        class="mx-auto shadow-sm border border-gray-100" />
    </picture>
  </figure>

</p>
<p>&hellip; Sadly, there is an extra layer of confusion.</p>
<h2 id="and-then-there-is-it4it">And then there is IT4IT</h2>
<p>So IT4IT storms the scene in 2015 and takes the idea of the separate units and dials it way up.</p>
<p>In this setup we take an IT unit and give them their own value steams, business capabilities, processes &hellip; basically everything a full organization has. This feels like the opposite of the previous section, because IT4IT decomposes IT into its own set of value streams and capabilities.</p>
<p>IT4IT splits the IT function into four end-to-end value streams (Strategy to Portfolio, Requirement to Deploy, Detect to Correct, Request to Fulfill). Each of these Value Streams is an implementation of other IT practices like <a href="https://framework.scaledagile.com/#big-picture"




 target="_blank"
 


>SAFe</a>, <a href="https://www.itil.com/"




 target="_blank"
 


>ITIL</a> and even <a href="https://www.opengroup.org/togaf"




 target="_blank"
 


>TOGAF</a> <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>.</p>
<p>Whether that&rsquo;s a good idea is not part of this post.</p>
<h2 id="so-in-short">So in short</h2>
<p>Value Streams and Value Chains are different concepts that handle different things.</p>
<p>If you are looking at end-to-end flows, you will be dealing in <strong>Value Streams</strong>. Here we try to map out these streams to optimize them and add SLA&rsquo;s. They cross business units so when you improve the maturity of one of these streams you handle multiple teams.</p>
<p>If you want to optimize the inner workings of business units themselves, you will be dealing with a <strong>Value Chain</strong>. These look at what a business unit does, how it does it, and what it needs. That value is combined into a Value Chain.</p>
<p>If you’re working with <strong>IT4IT</strong>, you model the IT department almost like its own miniature enterprise, complete with its own value chain and value streams.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>You can create low value or negative value, but that&rsquo;s more a failure in strategy or execution. Not really part of this conversation at this point&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Service Level Agreement: agreements on how something is done and what the requirements are of the end product or service&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://frederickvanbrabant.com/blog/2025-05-16-people-processes-technology-and-information/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2025-05-16-people-processes-technology-and-information/</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>I&rsquo;m not sure I would all label them IT practices&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Choosing your starting line in enterprise architecture</title><link>https://frederickvanbrabant.com/blog/2025-11-28-choosing-your-starting-line-in-enterprise-architecture/</link><pubDate>Fri, 28 Nov 2025 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2025-11-28-choosing-your-starting-line-in-enterprise-architecture/</guid><description>&lt;p&gt;I&amp;rsquo;ve been part of the creation of five enterprise architecture offices in my life. Some I’ve led, others I’ve simply been part of.&lt;/p&gt;
&lt;p&gt;If you start up an enterprise architecture office, you have two types of strategies people use. Some people start by mapping everything that exists, in whatever state it happens to be. They then assess what they have and start building a gap analysis towards a better, more uniform state.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>I&rsquo;ve been part of the creation of five enterprise architecture offices in my life. Some I’ve led, others I’ve simply been part of.</p>
<p>If you start up an enterprise architecture office, you have two types of strategies people use. Some people start by mapping everything that exists, in whatever state it happens to be. They then assess what they have and start building a gap analysis towards a better, more uniform state.</p>
<p>The other group of people start at the end point and work their way back. They sketch out the ideal state and map out the bare essentials towards getting there.</p>
<p>The funny thing is that this is one of the few times that architects don&rsquo;t say “it depends”. Most people have a very clear preference for one approach or the other.</p>
<p>Let&rsquo;s see how each approach works.</p>
<p>Please note: this article is mainly based on the business layer and strategy layer of enterprise architecture. So most of this discussion will be about things like Business Capabilities, Operating models, Value streams and the like.</p>
<h2 id="mapping-the-as-is">Mapping the AS-IS.</h2>
<p>My guess is that if you&rsquo;re tasked with setting up a new architecture office, the chances of the organization having a high architecture maturity are rather small.</p>
<p>That means that they might have a CMDB with applications <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> or an Excel file with a list of applications they use. They might have business lines in an organogram or some home-grown interpretation of capabilities or an operating model.</p>
<p>In the case of mapping out the AS-IS this is now your hunting ground. It&rsquo;s time to start interviewing people and hunting schemas and PowerPoints.</p>
<p>You will quickly find that whatever frameworks or models you’d like to use won’t fit the structures you’ll find. And worst of all the information you&rsquo;re finding is incomplete and incompatible with each other.</p>
<p>You might find business units that are linked to applications in the CMDB that are totally absent in the business units section of the strategy slides of a C-suit strategy meeting.</p>
<p>This is OK, you are mapping out the current way the organization works. If you find incompatibilities, that&rsquo;s actually a good thing. You are already creating value by pointing that out.</p>
<p>Once you have gathered “everything” <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> you can call that your baseline. Then it&rsquo;s time to try to mould this towards a structure that fits together. Connect the free-form jazz version of a capability map you found in a slide deck somewhere to industry standard value streams.</p>
<p>Once you have an overview of things you&rsquo;ve designed (or used standards for) to the things you have found, you can start to clean up and improve the AS-IS. Slowly but steadily towards a more mature model.</p>
<p>The big upside of this approach is that you are working with terms and information that is familiar to the organization. People will recognize the works you are linking applications and business units to, as they probably use them themselves.</p>
<h2 id="going-straight-for-the-to-be">Going straight for the TO-BE.</h2>
<p>The second option is not to bother with anything like that at all.
If you’ve gone through this ordeal once, you know it’s a frustrating exercise.</p>
<p>You will not find a capability map, you will find six. And the same people who use one of those six capability maps will happily create a seventh in their next strategy meeting.</p>
<p>That also means that the concepts you find aren’t actually carried or used in the organization. If you find a value stream outline in a management slide, chances are that&rsquo;s the first and the last time those value streams are shared.</p>
<p>Furthermore, the chances of these structures being made by a single person in half a day before a big meeting are also high.</p>
<p>The idea of skipping the AS-IS altogether comes down to: why base our architecture on structures that are not only, very low quality, they are also probably not carried in the organization.</p>
<p>It will take you, at least, six month to interview and gather all the stakeholders and information. That is an investment of six months into something that you want to move away from.</p>
<p>So here you start with your ideal structure that is based on frameworks or industry standard work. Things you know fit together and where, if you open an enterprise architecture book, you will recognize.</p>
<p>You will have to explain all these concepts and structures to the people you work with, but that was always going to be the case. The big difference here is that you are going to explain structures that you know are sound, and don&rsquo;t have to explain very shaky company structures that you found somewhere in a slidedeck from five years ago by some guy that doesn&rsquo;t even work here any more.</p>
<h2 id="and-what-about-applications-servers-and-data">And what about applications, servers, and data?</h2>
<p>I mentioned in the beginning of this article that this post is about the business and strategy layer, but I wanted to briefly touch on this as well.</p>
<p>Always use the AS-IS.</p>
<p>I&rsquo;ve seen people, when there was a big migration or merger going on, try to get away with only mapping out the TO-BE. That&rsquo;s a mistake.</p>
<p>Situations like migrations and mergers are where architects can bring a lot of value, it&rsquo;s often when they get hired.</p>
<p>Ignoring the entire setup because ‘it’s all going to change soon’ is skipping the work where architects can actually provide the most value.</p>
<h2 id="my-experiences-with-both">My experiences with both.</h2>
<p>As mentioned before, I&rsquo;ve done both in my life. When I started out in EA, I thought it was madness to just ignore the AS-IS of an organization and just dive into something totally new. I found it very arrogant to be honest.</p>
<p>Now that I&rsquo;ve had a bit more experience, I&rsquo;m starting to realize that if an organization has low maturity in these things, it&rsquo;s often not something that is very close to their heart. So why try to shoehorn these concepts into your strategy.</p>
<p>That being said, I&rsquo;m still an architect, so I have to say: It depends<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.
If you walk into an organization, and you see that senior leadership is constantly talking about their value streams. You should obviously adopt those. Even if they don&rsquo;t make sense or don&rsquo;t fit the frameworks you&rsquo;re used to.</p>
<p>The same with capability maps, if they are using a capability map as a central structure to report on, don&rsquo;t try to swap it out with a better one. They will never adopt your suggestion.</p>
<p>So here are my suggestions.
See what’s actually being used in the organization. If they work around a certain structure, adopt that AS-IS. You&rsquo;re not going to win that fight. But make the surrounding structure yours.</p>
<p>That means that if they have well-defined processes, you can link these to your own capability map to track maturity and report back on those. Don&rsquo;t go searching in archived PowerPoints for some capability map you might or might not find.</p>
<p>So yes, you can map the AS-IS. You can design the TO-BE. You can even claim you’re doing both, which is the classic architect escape hatch. But you will always have to convince people about the concepts you&rsquo;re using, even if it&rsquo;s concepts the organization is already using.</p>
<p>If something already exists and people use it, adopt it. If everything is a mess and nobody agrees on anything, skip the archaeology and design something that makes sense to you.</p>
<p>The rest is just iteration.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://frederickvanbrabant.com/blog/2025-11-15-the-cmdb-as-an-architecture-source/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2025-11-15-the-cmdb-as-an-architecture-source/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>This is a rabbit hole, you will always find more&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Even if I said in the beginning that it wasn&rsquo;t going to be the case here&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>The CMDB as an architecture source</title><link>https://frederickvanbrabant.com/blog/2025-11-15-the-cmdb-as-an-architecture-source/</link><pubDate>Sat, 15 Nov 2025 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2025-11-15-the-cmdb-as-an-architecture-source/</guid><description>&lt;p&gt;Every company that I&amp;rsquo;ve helped start their enterprise architecture practice so far, always tell me that they might not have architecture setup yet, but they do have a ton of information in the CMDB that we can use to kickstart the exercise.&lt;/p&gt;
&lt;p&gt;The CMDB is our source of truth of all the applications, servers, and it&amp;rsquo;s all linked to service lines and capabilities. Furthermore, it&amp;rsquo;s maintained by a team and it&amp;rsquo;s fully ITIL aligned. It&amp;rsquo;s a treasure trove!&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>Every company that I&rsquo;ve helped start their enterprise architecture practice so far, always tell me that they might not have architecture setup yet, but they do have a ton of information in the CMDB that we can use to kickstart the exercise.</p>
<p>The CMDB is our source of truth of all the applications, servers, and it&rsquo;s all linked to service lines and capabilities. Furthermore, it&rsquo;s maintained by a team and it&rsquo;s fully ITIL aligned. It&rsquo;s a treasure trove!</p>
<p>All of that is true. But the CMDB is a horrible place to base your architecture upon.</p>
<h2 id="what-is-a-cmdb">What is a CMDB?</h2>
<p>A CMDB is a catalogue in a ticketing system. The market leader in this space, and the one I keep bumping into, is ServiceNow.</p>
<p>These platforms started out as a way to support and track helpdesk/support issues. You have an issue with some software or hardware, you log a ticket in the system, and the engineers can get to work on that ticket.</p>
<p>Over time we all wanted more insights into these workflows so we started to add inventories to this process. A list of applications to link the bug or issues to, a list of service lines that needs to follow up on the ticket, the environment and server the application runs on, …</p>
<p>These days, applications like ServiceNow have so many extra inventories that it could do every thing in an organization … in a very mediocre way.</p>
<p>All of these lists are there to support the workflow of incident/change/bug/problem.</p>
<p>If you look at these inventories, you can quickly see a lot of the same material we use in architecture. It has a list of applications, this can be used as our application portfolio. These applications are linked to servers and environments, great, we already have a big part of our technical landscape mapped out!</p>
<p>These applications have owners, service lines and teams? Great, we have a business layer.</p>
<p>The issue appears when you take a deeper look at what&rsquo;s actually in these lists.</p>
<h2 id="the-conceptual-mismatch">The conceptual mismatch.</h2>
<h3 id="different-definitions">Different definitions</h3>
<p>If you take a look at the applications in the business application list you will start to scratch your head as an architect. You will bump into Word plugins, Excel sheets, websites, and even multiple parts of the same bigger application.</p>
<p>The same thing with Service Lines and Capabilities. Why does marketing have five service lines and IT thirty-five? Why are all these capabilities just things that IT does?</p>
<p><em>The answer is very simple, cause those are not architectural lists, they are lists to handle tickets and support issues.</em></p>
<p>They use the same words but with very different meanings.</p>
<p>If your business application list contains macros and Word plugins, any attempt at portfolio analysis becomes meaningless.</p>
<h3 id="different-rates-of-change">Different rates of change</h3>
<p>Every time a ticket comes in to a system that is not yet in the CMDB, a new business application will be created. If that new system does something that is hard to link to a current capability, there will be a new capability.</p>
<p>If it turns out that it&rsquo;s very important, a new team and service line might be created in the CMDB to support this.</p>
<p>The opposite is also true. If we no longer use an application, that can result in the removal of entire service lines and capabilities.</p>
<p>All of this can be done in the span of weeks. I&rsquo;ve seen capabilities created on a Monday and removed by Friday because someone needed the ticket to route correctly.</p>
<p>You don&rsquo;t want to base your enterprise architecture on capabilities that come and go.</p>
<p>Why are these lists so volatile?</p>
<p><em>The answer is very simple, cause those are not architectural lists, they are lists to handle tickets and support issues.</em></p>
<h3 id="different-structures">Different Structures</h3>
<p>Say you see information on the server layer of a CMDB that would actually be more suited on the application layer. Think of a version number of the application that runs on the server.</p>
<p>Moving that field to the application layer and removing it from the server layer would be a dangerous move. All of these lists have entire workflows behind them and are deeply relational. Moving a field might break structures all over the system.</p>
<p>The people that manage the CMDB know about these workflows, but you don&rsquo;t. It is a different domain after all.</p>
<p>Some of these fields that seemingly make no sense are actually mandatory from an ITSM point of view. They might even be duplicate fields on different layers with different information.</p>
<p>Why is this all so messy?</p>
<p><em>The answer is very simple, cause those are not architectural lists, they are lists to handle tickets and support issues.</em></p>
<p>


  <img src="/images/2025-11-15-The-CMDB-as-an-architecture-source/matrix.png" alt="On overview of how strategic architectural debt tickles down to the other layers" />

</p>
<h2 id="what-using-the-cmdb-as-your-architectural-source-of-truth-implies">What using the CMDB as your architectural source of truth implies</h2>
<p>Don&rsquo;t get me wrong, you can base your architecture upon this entire system. But in that case, you also have to realize the deeper implications.</p>
<p>If you take the structure of an IT service management as your base, you also position your entire IT department as a service to the rest of the organization.</p>
<p>Architectural models shape how an organization thinks about itself. If your model is rooted in ITSM, then everything becomes framed around incidents, support, and change control and not strategy, capabilities, or value creation.</p>
<p>Application rationalization, Capability maturity, and ROI calculations are out. Your main focus will be IT-architecture.</p>
<h2 id="i-do-use-the-cmdb-as-an-information-source">I do use the CMDB as an information source</h2>
<p>It would be madness to start from scratch. You can&rsquo;t just ignore all the information that is in a CMDB and go to every department and ask them what applications, services, and connections they have. That would be a massive waste of money.</p>
<p>CMDB teams do good work, it’s just a different domain.</p>
<p>I sync my enterprise architectural tool with ServiceNow <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, But not one to one. I sync the basic information: Name, description, technical/business owner, and other similar information directly to my business application components.</p>
<p>Then filter out the applications that I think are not (architectural speaking) business applications <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<p>The capabilities, Service Lines, Business Functions I skip. These concepts are often too incompatible so that&rsquo;s full manual work.</p>
<p>The other information that might be there in physical application layers I sometimes sync with a bronze data layer. A separate section in my architecture tool that is “dirty”. If your EA tool supports scripting, you can maybe start cleaning it op and sync it to your “clean” sections. It&rsquo;s regularly not clear if the value is there.</p>
<p>Integrations I never sync. These are too important and too central to a metamodel to just blindly take over. They can be wrong or outdated. They can be incomplete (they just map what is needed to handle the ticket), or very workflow specific.</p>
<h2 id="stop-trying-to-make-the-cmdb-something-it-isnt">Stop trying to make the CMDB something it isn’t</h2>
<p>A CMDB is a critical part of an IT organization, just as a tool like ServiceNow<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>. But these tools are there to fill a specific role, and that role is not enterprise architecture.</p>
<p>It&rsquo;s very easy to look at these repositories and think that you don&rsquo;t have to do the hard work of gathering all that information. But sadly, that&rsquo;s not how it works. Believe me, I&rsquo;ve been there.</p>
<p>Your architecture tool should not ignore all the information that exists in these applications, but you should be very aware of your source.</p>
<p>If you are just making a carbon copy of your CMDB, you might as well directly do your architecture in ServiceNow. And I&rsquo;m not sure whether you&rsquo;re going to have a great time. You will be fighting the tool and structure more than you’ll be doing actual architecture.</p>
<p>Architecture should be able to evolve as the business evolves. A CMDB can’t follow you there.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Almost all EA tools can do this&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>This is a huge topic but very broadly speaking:</p>
<ul>
<li>Active Directory, Enterprise Service Bus (Mulesoft etc) = Infrastructure</li>
<li>Excel sheets = maybe artifacts, but I mostly don&rsquo;t implement it</li>
<li>Different applications in a suite = I use the C4 model for this</li>
</ul>
&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></li>
<li id="fn:3">
<p>I really don&rsquo;t like ServiceNow&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Architectural debt is not just technical debt</title><link>https://frederickvanbrabant.com/blog/2025-10-31-architectural-debt-is-not-just-technical-debt/</link><pubDate>Sat, 01 Nov 2025 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2025-10-31-architectural-debt-is-not-just-technical-debt/</guid><description>&lt;p&gt;When I was a developer, half of our frustrations were about technical debt (the other were about estimates that are seen as deadlines).&lt;/p&gt;
&lt;p&gt;We always made a distinction between code debt and architecture debt: code debt being the temporary hacks you put in place to reach a deadline and never remove, and architectural debt being the structural decisions that come back to bite you six months later.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>When I was a developer, half of our frustrations were about technical debt (the other were about estimates that are seen as deadlines).</p>
<p>We always made a distinction between code debt and architecture debt: code debt being the temporary hacks you put in place to reach a deadline and never remove, and architectural debt being the structural decisions that come back to bite you six months later.</p>
<p>While I agree that implementing software patterns like the strangler pattern or moving away from singletons is definitely software architecture. Architectural debt goes way beyond what you find in the code.</p>
<h2 id="how-i-see-technical-architectural-debt-these-days">How I see technical architectural debt these days.</h2>
<p>As an enterprise architect I still mostly complain about architectural debt and estimates that are seen as deadlines. That much certainly hasn’t changed.</p>
<p>These days, I’m less concerned with how the software itself works. That’s just not feasible when your organization has hundreds of applications <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. My main concerns are more about how these applications interact with the rest of the landscape. How the data flows, where data lives, whether there are bottlenecks, who&rsquo;s going to maintain it, and what role will this application have in the future.</p>
<p>In an enterprise environment this is inevitable. There are so many applications and more than half of them are 3rd party SaaS applications. You need to keep on top of what you can control and let go of the parts you can&rsquo;t.</p>
<h2 id="the-layers-of-architectural-debt">The layers of architectural debt</h2>
<p>But architectural debt goes way beyond the technical layers. Enterprise architecture is not technical architecture <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<p>And yes architectural debt can cost an organization a lot of headaches, but architectural debt on business and strategy layers can do even more damage.</p>
<p>Let&rsquo;s dive into the issues at every layer.</p>
<p>


  <img src="/images/2025-10-31-Architectural-debt-is-not-technical-debt/layers.png" alt="The information above in a diagram" />

</p>
<h3 id="application--infrastructure-layer"><strong>Application / Infrastructure layer</strong></h3>
<p>As discussed in the intro, EA shouldn&rsquo;t focus on the code level. I think that&rsquo;s the job of software developers/software leads. They are in the code day to day, you are not. You can suggest software patterns or ideas (think event sourcing for applications that could be in the problem space), but they decide how they want to structure and maintain the code.</p>
<p>Instead, I focus on things like integration patterns. Do we still drop files over sFTP while also using a REST API? Maybe we can bring everything towards REST so we have a unified approach.</p>
<p>What about the systems we have running, what do they do? If there is overlap in functionality, then we can bring them together. Say for example file storage (a classic example). You might have free Sharepoint storage with every Microsoft account, but you might also rent storage space at AWS. Perhaps you could combine those.</p>
<p>And don&rsquo;t forget about vendor lock-in. Finding the right balance between getting a discount for getting a lot of services from the same vendor, and them locking you in and holding your systems hostage behind huge migration costs is a real problem a lot of companies struggle with.</p>
<p>At this layer all the debt directly impact operations. Costs go up and time of delivery increases.</p>
<p>


  <img src="/images/2025-10-31-Architectural-debt-is-not-technical-debt/eavssa.png" alt="The focus of an EA is longer term and less focused compared to a SA" />

</p>
<h3 id="business-layer"><strong>Business layer</strong></h3>
<p>Just like in the application/infrastructure layer we are not focused on the details. We don&rsquo;t care about how many people work in what department and how the roles are defined.</p>
<p>What we are focused on is things like ownership and stewardship. If an application or process goes down, if there is a data leak, who are you going to contact?</p>
<p>Who are the people you need to get in a room if you want to modernize a department? Sure, you want to talk to the head of the department and the stakeholders of that department, but every department work with other departments. Just like applications, these processes, and flows are interconnected. You don&rsquo;t want to modernize the processes of one department and break the day to day of five others.</p>
<p>What about outdated or phantom processes? If you give a new hire a business process overview to help them learn their role, and that documentation is wrong, that’s unpleasant. What&rsquo;s even more unpleasant is if instead of a new hire, it&rsquo;s an auditor.</p>
<p>So at this level it&rsquo;s more about documentation. Outlining how departments work, who does what and what they need to do their job. If people work under wrong assumptions, they will also start projects under wrong assumptions. They will already be on the back foot before they start.</p>
<p>So here again it&rsquo;s about cost and outside risk. Issues here will multiply issues on the operational side.</p>
<h3 id="strategy-layer"><strong>Strategy layer</strong></h3>
<p>An enterprise architect doesn&rsquo;t build strategy, they assist the people that define the strategy.</p>
<p>To do that however, you need to have your rows in order. The better insights you can provide, the better the outcome (potentially) will be.</p>
<p>The main issues of debt here are mis-definition and half frameworks. I&rsquo;ve waxed on and on about the value of Business-Capability maps<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> for this exact reason.</p>
<p>If you want to understand what your organization is good or bad at, you can do a business capability maturity map. A huge exercise where you go over every capability and rate them with a scoring of chosen parameters.</p>
<p>Every capability now has a score, and we can start looking at what areas we need to focus on and improve (if they fit in our strategy for where we see the company).</p>
<p>Great! But what if we defined the capabilities wrong? Maybe the map is outdated, or it&rsquo;s not a capability map at all but just a silo&rsquo;d business department overview. In that case, you&rsquo;re going to base your strategy for the next 3-5 years on wrong assumptions.</p>
<p>Debt at this level can have enormous long-term consequences. This is what blocks transformation exercises and could even make bad long term strategy look appealing.</p>
<p>


  <img src="/images/2025-10-31-Architectural-debt-is-not-technical-debt/cascade.png" alt="On overview of how strategic architectural debt tickles down to the other layers" />

</p>
<h2 id="tackling-architectural-debt">Tackling architectural debt</h2>
<p>Luckily, there’s some good news: unlike developers, enterprise architects have the time and visibility to flag architectural debt. We have the time and resources to create dashboards and gather proof of what is going on. We also have access to the right people where we can plead our case to.</p>
<p>And that is also how and what we should do. Showing misalignments to a CIO or COO is easier with a PowerPoint or dashboard, than with a God class that you want to refactor into a command-bus pattern.</p>
<p>Keeping stock is key. I have in my architecture tools clearly laid out what I consider debt. I also have a list in my notebook of things I want to tackle and the order in what I want to tackle them in.</p>
<p>Building that case is bringing all the data you have gathered together, an AS-IS and a TO-BE, and a business case of what the improvements would be, and the risks of not handling it.</p>
<p>From there, it’s mostly about picking your battles. There are places where it&rsquo;s easier to tolerate architectural debt, think for example of a system of innovation versus a system of record. <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> You can tolerate it more in innovation projects than on core sources of truth. On the condition that they’re handled once the innovation period is over.</p>
<p>Also be prepared that most often you will get the answer: OK, you can go out and fix it. So be sure you have the bandwidth to actually go out and fix it. The debt will not just disappear by you just flagging it.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I wrote about the abstraction you have to make here: <a href="https://frederickvanbrabant.com/blog/2025-01-17-turning-complexity-into-manageable-complication/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2025-01-17-turning-complexity-into-manageable-complication/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://frederickvanbrabant.com/blog/2025-04-14-avoiding-vague-hand-waves-what-is-enterprise-architecture/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2025-04-14-avoiding-vague-hand-waves-what-is-enterprise-architecture/</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<ul>
<li><a href="https://frederickvanbrabant.com/blog/2024-07-12-business-capabilities-how-i-like-to-use-them/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2024-07-12-business-capabilities-how-i-like-to-use-them/</a></li>
<li><a href="https://frederickvanbrabant.com/blog/2024-12-23-enterprise-architecture-is-really-bad-at-architecture-strategy/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2024-12-23-enterprise-architecture-is-really-bad-at-architecture-strategy/</a></li>
<li><a href="https://frederickvanbrabant.com/blog/2025-03-09-enterprise-architecture-skunk-works/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2025-03-09-enterprise-architecture-skunk-works/</a></li>
</ul>
&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></li>
<li id="fn:4">
<p><a href="https://frederickvanbrabant.com/blog/2025-08-10-pace-layering-an-application-portfolio/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2025-08-10-pace-layering-an-application-portfolio/</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Nemawashi and the Meta of Meetings</title><link>https://frederickvanbrabant.com/blog/2025-10-17-nemawashi-and-the-meta-of-meetings/</link><pubDate>Fri, 17 Oct 2025 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2025-10-17-nemawashi-and-the-meta-of-meetings/</guid><description>&lt;p&gt;A few weeks ago I walked into a meeting room to discuss my solution to a fork in the road problem. We&amp;rsquo;ve been roadblocked for two weeks with two clear paths forward. One of them I preferred; a bit more work, but it would bring dividends in the future and not burden us with a less than ideal solution. The other way forward was what a different group wanted: to push the project through so they could reach the deadline.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>A few weeks ago I walked into a meeting room to discuss my solution to a fork in the road problem. We&rsquo;ve been roadblocked for two weeks with two clear paths forward. One of them I preferred; a bit more work, but it would bring dividends in the future and not burden us with a less than ideal solution. The other way forward was what a different group wanted: to push the project through so they could reach the deadline.</p>
<p>I came armed with slides, data, and what I thought was a solid argument, only to realize halfway through that the decision had already been made. The executives weren’t just leaning toward the other solution; they were quoting that team’s talking points word for word.</p>
<p>Everyone&rsquo;s mind was already made up, just not in my favour. They’d clearly done the groundwork and convinced every one of their approach long before I entered the room.</p>
<p>It took me a while to realize what had happened.</p>
<p>What happened is called Nemawashi. A Japanese term literally meaning “turning the roots”, referring to preparing the soil before transplanting a tree.</p>
<p>Not something that is unique to Japanese culture.</p>
<h2 id="the-myth-of-the-meeting-room">The myth of the meeting room.</h2>
<p>Nemawashi can be seen as backroom politics, but that shouldn&rsquo;t be the case. The idea is that you check in before a big decision with all the stakeholders and go over all the points one on one. Making sure everyone fully understands what needs to be discussed and where everyone stands.</p>
<p>Afterwards, when the real meetings start, everyone is fully informed, had time to think it over and there are no surprises.
That way there are no open disagreements and losing of face, everything is discussed in private.</p>
<p>The big meeting room gathering is more of a ceremony to adopt the solution.</p>
<p>This seems like a big time waster, but you have to think to yourself how many meetings you&rsquo;ve been part of that resulted in a big emotional discussion that goes nowhere. It&rsquo;s not easy to have the data ready to defend yourself to a discussion that ambushed you. And while people are arguing and trying to find the data, the rest of the meeting is just waiting for the follow-up meeting to further discuss the way forward.</p>
<p>Of course, this can look like office politics if done badly: if it’s about exclusion instead of inclusion. But that’s not Nemawashi.</p>
<p>The point isn’t to stack the deck, it’s to make sure everyone had a chance to play. Just look at the intro of the post. One person (or group) was left out of the discussion, and it&rsquo;s an unfair fight.</p>
<p>But that&rsquo;s also because the other side didn&rsquo;t partake in the practice.</p>
<p>To me, we sometimes see meetings as a race for quick wit and eloquence. You go to a meeting room, and you all get the same information to a problem at the same time and it&rsquo;s Ready, set, go!</p>
<p>The first person to quickly and convincingly suggests a solution wins. It is not often you see people react with “hmm, I really need to think this over and look at some data”. That obviously happens, but in my opinion way too seldom.</p>
<p>This is how you get quick fixes that take the road of least resistance. If you have no data and time to think you will go for shallow solutions.</p>
<h2 id="nemawashi-means-celebrating-the-roots-not-the-fruit">Nemawashi means celebrating the roots, not the fruit.</h2>
<p>Another side effect of Nemawashi is that the big grudges fall away.</p>
<p>Humans don’t think well in public. We anchor to the first opinion we hear, we defend our status, and we rarely admit uncertainty in front of peers. Nemawashi respects that, it lets people think in private before they decide in public.</p>
<p>Open disagreements, especially when people are watching, can feel very confrontational.
Walking into a meeting room and seeing the same people that you had a big emotional discussion with a few days ago will colour the mood of the meeting instantly.</p>
<p>The opposite is also true. It might not be the best idea to publicly disagree with your lead in every public setting. These are things that are best done before big meetings and signal a unified department to the rest of the organization.</p>
<p>Having a one-on-one with someone you don&rsquo;t like, that has very opposing views, is probably also best done before the meeting. You will notice that a more informal setting will make these conversations more palatable. Especially compared to these kinds of meetings where the CIO or COO is also in the same room.</p>
<p>In the end, it&rsquo;s also not about winning or losing. It&rsquo;s about getting workable solutions out of the door. These often happen when everyone is heard and had time to check the data.</p>
<h2 id="less-talking-more-thinking">Less talking, more thinking</h2>
<p>Ultimately this isn&rsquo;t about some corporate secret cheat code to fix your meetings. It&rsquo;s more a mindset switch.</p>
<p>The highest value decisions are not made in a sudden public performance, but made in quiet, deliberate preparation.</p>
<p>Removing the culture of quick-draw arguments and fast wit solutions in favour of more one-on-one data sharing and discussions will allow us to make more sustainable decisions.</p>
<p>This might all feel like a big investment of time. Way more than just one big meeting and maybe a follow-up. But you have to keep in mind that the time in meetings and one-on-ones is nothing compared to the time the solution will be in place.</p>
<p>I’d rather spend an extra hour at the coffee machine with someone, talking over a problem, than having to support a badly thought out solution for the next four years.</p>
]]></content:encoded></item><item><title>Solution designs should only be a few pages</title><link>https://frederickvanbrabant.com/blog/2025-10-03-solution-designs-should-only-be-a-few-pages/</link><pubDate>Fri, 03 Oct 2025 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2025-10-03-solution-designs-should-only-be-a-few-pages/</guid><description>&lt;p&gt;Architects always get a bad rap when it comes to design documents.&lt;/p&gt;
&lt;p&gt;We get blamed for delivering these massive solution design documents that take weeks to produce, eat into the time for development and in the end result into an encyclopedia that nobody reads.&lt;/p&gt;
&lt;p&gt;The architectural drive to document and cover as much as possible naturally evolves into templates that are in the 40–50 pages (template alone).&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>Architects always get a bad rap when it comes to design documents.</p>
<p>We get blamed for delivering these massive solution design documents that take weeks to produce, eat into the time for development and in the end result into an encyclopedia that nobody reads.</p>
<p>The architectural drive to document and cover as much as possible naturally evolves into templates that are in the 40–50 pages (template alone).</p>
<p>Something I think we should move away from.</p>
<h2 id="why-compact-solution-designs-work-better">Why compact Solution Designs work better.</h2>
<p>If you&rsquo;ve ever been tasked on filling out these massive templates, you know that they are a cooperative exercise. Sure, a lot of architectural meetings happen during the design phase, but some parts are filled in by people from the business and some are filled in by subject-matter experts (SME&rsquo;s).</p>
<p>Often what happens is that there is a lot of copying and pasting from other documents and “not applicable”<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> sections. That&rsquo;s expected, if it&rsquo;s not your job to fill out these intimidating documents it&rsquo;s very tempted to just go with the bare minimum and go back to your real work.</p>
<p>So here is my claim for compact solution designs: Let&rsquo;s make them as lean as we can, while still covering the important parts, and add more information if the project requires them during the project phase.</p>
<p>That way, we have:</p>
<ul>
<li>Less copy-pasting from other documents: just reference them if needed.</li>
<li>They are faster to produce, we don&rsquo;t eat too much into development time.</li>
<li>Easier to maintain: clear overview what goes where, and you can actually know the entire document.</li>
<li>People actually read it: should be readable in an hour.</li>
<li>People don&rsquo;t get burned out on these overly complex documents.</li>
</ul>
<h2 id="what-belongs-in-a-good-solution-design">What belongs in a good Solution Design</h2>
<p>First we need to know who the core audience is for our document. In the case of a solution design this is almost always technical people that are going to implement the solution.</p>
<p>So what does a technical person need to know to implement a solution?</p>
<ul>
<li>What are we building?</li>
<li>Who is going to work on it?</li>
<li>How long do we have?</li>
<li>How are we going to build it?</li>
<li>how are we going to maintain it?</li>
<li>What can I use?</li>
</ul>
<p>This is an actual document that technical people want to read, as it brings value. Don&rsquo;t go over licensing costs or vendor solvability in this document. That&rsquo;s not the focus of the people that are reading that document at that time. Do capture it in a different document (business case) and if there is an impact on the project, reference it, just don&rsquo;t include it in your base template.</p>
<p>If your document captures everything, it captures nothing. As people will not take three days out of their workday to go over the entire document. And even if they did, they will not remember it.</p>
<h2 id="my-design-document-structure">My Design Document structure.</h2>
<p>My document is segmented in five parts, and they all cover different areas. The idea of the document is that everyone (technical) in the project reads it cover to cover, but it still categorizes information in chapters.</p>
<h3 id="introduction">Introduction</h3>
<p>This is the setting the scene part of the document.</p>
<p>I like to start with a summary of the business case. Not a full-blown six pages deep dive, rather an executive summary. Something like half a page.</p>
<p>A big who-is-who is always welcome. I think a <a href="https://en.wikipedia.org/wiki/Responsibility_assignment_matrix"




 target="_blank"
 


>RACI</a> makes a lot of sense here. Don&rsquo;t forget about 3rd party people and vendors.
A timeline of the project would also fit here.</p>
<h3 id="application-architecture">Application Architecture</h3>
<p>The future state of the application goes here. This can go on for a few pages. I advise for the usage of diagrams over big blocks of text. Diagrams are easier to share and less intimidating to read.</p>
<p>The biggest challenge in this chapter is deciding the detail level.</p>
<p>It would be incredibly rare (if ever) that you should go to the level of ERD or class diagrams. Only if you are doing something incredibly exotic, you could maybe work a use case out at that level. But for everything else, it&rsquo;s a bad idea.</p>
<p>You don&rsquo;t have the upfront knowledge to work it out to that level, and it will change anyway during the project. Just trust your team to come up with a decent solution at that level during the project. They are hired because they are smart, so let them be smart.</p>
<p>The level I would focus on is the integration level. Container/Component level in the <a href="https://c4model.com/"




 target="_blank"
 


>C4 model</a>. Draw them out in a schema, annotate them in text below.</p>
<h3 id="data-architecture">Data Architecture</h3>
<p>This is actually something that I often see missing from those big solution architecture documents; the data layer. Mapping out the data that flows between integrations, what format, and mapping is really handy to know.</p>
<p>If you&rsquo;ve labelled the connections in the Application Architecture chapter, you could use those labels to go over the data that flows between them. This makes a lot of sense to display in a table.</p>
<p>If you have an ESB (<a href="https://en.wikipedia.org/wiki/Enterprise_service_bus"




 target="_blank"
 


>Enterprise service bus</a>) a translation table could be a nice addition. What maps to what.</p>
<p>I think a subchapter on Identity and Access Management is probably also not out of place here.</p>
<h3 id="technology--infrastructure">Technology &amp; Infrastructure</h3>
<p>A lot of companies use standardized setup&rsquo;s for their applications, but it&rsquo;s not a bad idea to give an overview.</p>
<p>Again, the level of detail is something to keep in mind. Don&rsquo;t dive into the nitty-gritty here (unless it is needed in this project), just give an overview.</p>
<p>Don&rsquo;t forget to outline the different environments (dev, UAT, prod) and what is included in what.</p>
<h3 id="appendix">Appendix</h3>
<p>This is the it-didn&rsquo;t-fit-in-the-other-chapters chapter. You could put architectural considerations/decisions here. “We know X is not the best way to do it, but we did it anyway for this and this reason.”</p>
<h2 id="the-takeaway-philosophy">The takeaway philosophy</h2>
<p>I rather have a less detailed, but still covering the essence, document than a fully encompassing document, that covers everything, but that nobody reads.</p>
<p>I wrote in the past about <a href="https://frederickvanbrabant.com/blog/2024-07-19-architecture-in-an-agile-world/"




 target="_blank"
 


>the role of an architect in a modern organization</a> and I think this is very much part of it. Architecture offices shouldn&rsquo;t be ivory tower groups that only produce big tomes of acronyms. They should be supporting teams with information about what is our strategy going forward and what is happening in different parts of the organization.</p>
<p>So slim down your documents and use that time to be more involved in the rest of the work. There are still a lot of questions that pop up during development that need answers, and people are not going to be looking for them in a document they forgot existed.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>They often are very applicable&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Following processes won't make you a robot</title><link>https://frederickvanbrabant.com/blog/2025-09-19-following-processes-wont-make-you-a-robot/</link><pubDate>Fri, 19 Sep 2025 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2025-09-19-following-processes-wont-make-you-a-robot/</guid><description>&lt;p&gt;Every time I go to teams and start talking about process mapping and standard operating procedures (SOP) I notice an undeniable amount of unease like it just got a few degrees colder.&lt;/p&gt;
&lt;p&gt;What people hear isn’t &lt;em&gt;“we’re here to understand your work and make it smoother.”&lt;/em&gt;&lt;br&gt;
What they really hear is: &lt;em&gt;“We’re here to judge, strip the creativity, maybe even replace you with a machine.”&lt;/em&gt;&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>Every time I go to teams and start talking about process mapping and standard operating procedures (SOP) I notice an undeniable amount of unease like it just got a few degrees colder.</p>
<p>What people hear isn’t <em>“we’re here to understand your work and make it smoother.”</em><br>
What they really hear is: <em>“We’re here to judge, strip the creativity, maybe even replace you with a machine.”</em></p>
<p>I very much understand where the sentiment comes from, but that is very much not the idea behind a standard operating procedures and process mapping. It&rsquo;s actually quite the opposite.</p>
<h2 id="what-are-sops">What are SOP&rsquo;s?</h2>
<p>Think of an SOP as a cooking recipe. Not the kind you tear off the back of a meal kit, something closer to Kenji López-Alt’s style, the kind that tells you <em>why</em> every step matters with decent thought out details.</p>
<p>A good SOP covers the outcome we want, the steps to get there, the roles involved, and the expected result. Ideally, it also tracks updates and changes, so the process evolves alongside the organization.</p>
<p>In many industries like healthcare, security, manufacturing, &hellip; you don’t get a choice. Regulators require SOPs, and audits enforce them. That’s the compliance side.</p>
<p>Beyond compliance, mapping work across teams creates a kind of contract. Everyone knows what’s being done, how it changes hands, and what “done” really looks like. You create a shared understanding of not only your work, but also the work that comes before and after.</p>
<p>When new people start, you can use the SOP as part of their onboarding<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. This makes onboarding people so much easier and makes sure that there are no big hiccups in quality when roles change.</p>
<h2 id="stability-enables-creativity">Stability Enables Creativity.</h2>
<p>If I describe it like that SOPs sound like they box people in, giving the company all the control and leaving workers with nothing but routine. But that’s not the deal.</p>
<p>Let&rsquo;s look at it differently, back to the recipe example:</p>
<p>Every Tuesday for years, you’ve made the same pot of chilli. By feel, no recipe needed. Then one day, you order chilli at a restaurant, and it blows yours out of the water. So you want to improve your version.</p>
<p>To do that, you require consistency. By standardizing the process: measuring ingredients, tracking steps. You create a repeatable baseline. And from there, experimentation becomes meaningful. Add espresso, swap peppers, try something new, and you can tell exactly what effect it had.</p>
<p>What we&rsquo;ve described above is a form of <a href="https://en.wikipedia.org/wiki/Kaizen"




 target="_blank"
 


>Kaizen</a> taking a process and, just by doing it, improving it very slightly to get a better result.</p>
<p>That is exactly what we want to achieve with work like this. We want to take a look at the work that&rsquo;s currently being done, document it, and make sure the other work aligns to it by lowering the amount of overhead.</p>
<h2 id="focusing-on-the-creative-part">Focusing on the creative part.</h2>
<p>And what about the day to day then? It&rsquo;s nice that you can improve the flow, but you&rsquo;re not going to improve the flow every day, what about the current job?</p>
<p>An SOP is not intended to map out the process 100%. It&rsquo;s only intended to outline the base work. If part of your job is to go out and find the best vendors for retail, the SOP will not tell you what should be in your mails or how you do your market research.</p>
<p>It will just tell you to contact the vendors after you&rsquo;ve done the market research. Maybe it can automate or streamline the boring parts of your job so you can focus on the interesting stuff.</p>
<p>If your job is fully standardized in a fully repeatable process that takes no creativity, problem-solving, or communication, then it might indeed be fully automated.</p>
<p>I personally haven&rsquo;t seen a lot of those roles in a modern organization. In those cases I&rsquo;ve always seen people being offered alternative roles (with re-training) that rely more on their problem-solving skills. You could argue that would be an upgrade.</p>
<h2 id="sops-as-memory-of-innovation">SOPs as Memory of Innovation</h2>
<p>If you keep a change log of your SOP&rsquo;s, you also get snapshots of your organization over time, you’re building a time machine.</p>
<p>This is a very intriguing concept if you think about it. This can help explain, over the years, why some ways of working are in place<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<p>It can even help organizations reach back to procedures of the past. Say that there is a housing crash that reminds everyone of the one we had in 2001. Well let&rsquo;s look what we did in 2001 to counteract these issues and what effect they had.</p>
<p>You probably wouldn’t reinstate those exact processes. But by having that history documented, you gain insights that can inform today’s decisions. SOPs, in this way, preserve lessons that might otherwise be forgotten.</p>
<h2 id="sops-as-a-way-to-cement-your-role">SOP&rsquo;s as a way to cement your role</h2>
<p>I use my mapped out processes to go to the rest of the organization to tell them what I can help them with. They function as an internal marketing tool for my department.</p>
<p>They also give me leverage. When I ask another team for data in a certain format, I don’t sound picky, I can point to the process and say, <em>“Here’s the full process, and we use this format”</em>.</p>
<p>I understand that there is a negative sentiment regarding process mapping and SOP, especially with the rise of LLM&rsquo;s. <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>. But I think it&rsquo;s a reputation issue that focuses on the wrong perspective of the task.</p>
<p>They are mainly in place to automate the boring parts, so there is more focus on the important parts. And in regulated industries, they’re not optional anyway.</p>
<p>If you play your cards right, you can benefit greatly from documenting and reviewing them. They can make your job more interesting and give you extra eyeballs and tools in the future.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Don&rsquo;t just point them to the document and have them figure it out, guide them through it&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>This counters Chesterton&rsquo;s fence: <a href="https://frederickvanbrabant.com/blog/2025-07-11-chestertons-fence-and-paralysing-your-organization/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2025-07-11-chestertons-fence-and-paralysing-your-organization/</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>You can&rsquo;t train an LLM on the SOP. They are not detailed enough and the real valuable/ problem-solving parts are not covered.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Teams Outlast Projects</title><link>https://frederickvanbrabant.com/blog/2025-09-05-teams-outlast-projects/</link><pubDate>Sun, 07 Sep 2025 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2025-09-05-teams-outlast-projects/</guid><description>&lt;p&gt;In my startup days, we often had these dead marches where we had to implement a massive new feature in time before some kind of trade show happened. We worked long hours and even weekends to make it happen so the boss and a project manager could go to Vegas for the trade show.&lt;/p&gt;
&lt;p&gt;When they came back on Monday, they told us what the competition was doing, and laid out a brand-new strategy they’d dreamed up between hotel bars and expo booths.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>In my startup days, we often had these dead marches where we had to implement a massive new feature in time before some kind of trade show happened. We worked long hours and even weekends to make it happen so the boss and a project manager could go to Vegas for the trade show.</p>
<p>When they came back on Monday, they told us what the competition was doing, and laid out a brand-new strategy they’d dreamed up between hotel bars and expo booths.</p>
<p>That company no longer exists. Not because the strategies were wrong, or even the timelines. It failed because those plans were made in isolation, disconnected from a team that was already exhausted and out of motivation.</p>
<p>Projects execute strategy. People execute projects. And no project exists in isolation, they rest on everything that came before.</p>
<h2 id="strategy-is-not-self-executing">Strategy is not self-executing.</h2>
<p>You can make the most perfect strategy and roadmap in the world, but if you don&rsquo;t have the people to carry it out, then all you have a nice business case. An idea, not a project.</p>
<p>When we define projects, we often reduce people to headcount. “Three developers, one project manager, one ops person. Who’s available?”. But availability isn’t the same as readiness. A burned-out team won’t produce a winning outcome, no matter how strong the plan.</p>
<p>A good manager will look at the project and the team and assign people that might have done something like that before, or someone that can learn from the project. Yet we never look at the strategy or project planning phases if it is actually currently the right time to implement this based on the team&rsquo;s morale or setup.</p>
<p>There is a famous book in management called “Playing to Win: How Strategy Really Works” by Alan G. Lafley. In the book the steps of coming up with strategy and how to implement it are described.</p>
<p>One of the steps in the process is “What capabilities must we have in place to win?”. The book mainly explores things like “do we have the right people and processes”, but It never seems to go very deep into, What is the status of said people and processes.</p>
<p>A Formula 1 car is built to win, but if the fuel tank is empty, you&rsquo;re not exactly going to win SPA.</p>
<h2 id="i-hate-the-term-resources-for-people">I hate the term “resources” for people.</h2>
<p>People always look at me as I&rsquo;m the biggest hippie alive when I tell them I prefer the term people over the term resources in planning meetings. It treats them as interchangeable units that reset after each project.</p>
<p>Teams provide continuity in your organization. Projects come and go, but the same people will handle them stay behind<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. They carry the learnings, network, and practices from previous projects, but also the energy level and issues. High or low.</p>
<p>Broader organizational context matters, too. A rumour of layoffs, true or not, changes how people approach their work. Some push harder to prove their value. Others disengage and update their CV.</p>
<p>Long before the first kick-off meeting, the tone of a project is already set by the state of the team.</p>
<h2 id="yes-but-how-do-we-work-this">Yes, but how do we work this?</h2>
<p>As Alan Lafley reminds us: know your playing field. Strategy isn’t just about spotting opportunities, it’s about knowing when it’s the right moment to pursue them. Timing is as critical as vision. Acting too early can be as costly as acting too late, because execution depends not only on the market but on the state of the people who must do the work.</p>
<p>That doesn’t mean discarding ideas. Every organization should maintain a backlog of opportunities, a kind of “strategic inventory.” But treating that backlog as a to-do list to be executed immediately is dangerous.</p>
<p>The backlog is not a queue; it’s a library. Its value comes from being ready when the organization has the capacity and capability to act, not from sheer volume or speed of throughput.</p>
<p>When the timing is right, build the project carefully. Resist the temptation to fill roles by simply matching open availability to job titles. Instead, consider who has the relevant experience, who is motivated to grow, and who has the current energy to succeed.</p>
<p>Remember: a dysfunctional team produces a dysfunctional project. You can have the clearest strategy, the best tools, and the most generous budget, but if the team is exhausted, misaligned, or disengaged, the project will fail.</p>
<p>Sometimes the best move is to postpone a flagship initiative and focus on smaller efforts until the team is ready. It’s better to arrive later with a strong product than to rush something weak that damages both reputation and momentum.</p>
<p>In the long run, advantage comes not from speed alone, but from consistency, trust, and resilience. A delayed project can still succeed if it ultimately delivers value and strengthens the team. A broken project, delivered too soon, can erode customer confidence, damage reputation, and demoralize employees in ways that are far harder to repair.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I wrote about it here: <a href="https://frederickvanbrabant.com/blog/2025-04-5-business-strategy-is-like-a-lizard/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2025-04-5-business-strategy-is-like-a-lizard/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>How teams grow organically</title><link>https://frederickvanbrabant.com/blog/2025-08-22-how-teams-grow-organically/</link><pubDate>Thu, 21 Aug 2025 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2025-08-22-how-teams-grow-organically/</guid><description>&lt;p&gt;I&amp;rsquo;ve been working a lot with service line architecture recently. If you&amp;rsquo;re not familiar with that; it&amp;rsquo;s how business units such as IT, HR, or Sales bring services to clients, both internal and external. These structures often mirror team organization.&lt;/p&gt;
&lt;p&gt;Think of it as a hierarchy: IT at level one, Software Development and Ops at level two, and then individual teams, like: Software Team X or Ops Team Y, at level three.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>I&rsquo;ve been working a lot with service line architecture recently. If you&rsquo;re not familiar with that; it&rsquo;s how business units such as IT, HR, or Sales bring services to clients, both internal and external. These structures often mirror team organization.</p>
<p>Think of it as a hierarchy: IT at level one, Software Development and Ops at level two, and then individual teams, like: Software Team X or Ops Team Y, at level three.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>What’s surprising is how rarely these structures reflect reality. In practice, I often see people from multiple teams collaborating informally on projects. Even more striking, these same ad hoc groups reappear in project after project, suggesting a pattern that isn’t captured in the formal architecture.</p>
<p>If you&rsquo;re a bit familiar with domain driven design, you might see some parallels with Conway&rsquo;s law. And that is exactly what we&rsquo;re going to talk about.</p>
<h2 id="conways-law">Conway’s Law</h2>
<blockquote>
<p>Organizations which design systems (in the broad sense used here) are constrained to produce designs which are copies of the communication structures of these organizations.</p></blockquote>
<p>This is Conway’s Law, most typically applied to software systems and products. In essence, products are a direct reflection of the communication patterns within the teams that created them. How they think, talk and view the world.</p>
<p>I want to take it one step deeper, I believe that organizations themselves are defined by these communication structures. As a result, the products they deliver inevitably embody those same patterns.</p>
<p>The challenge is that these communication networks are informal, fluid, and nearly impossible to map. Yet they are precisely what shape the outcomes.</p>
<h2 id="how-teams-actually-form">How teams actually form</h2>
<p>Here’s how it usually starts. You’re standing in line for coffee and run into someone from finance. A little small talk, a joke or two, and then they mention it’s tax season. They’ve hired a student to shuffle CSV files from one server to another by hand.</p>
<p>You blink. <em>By hand?</em> Why not automate it? You recall that Michelle from development built a similar integration recently, maybe you rope in a business analyst. Very quickly, what began as a casual exchange becomes the start of a new project. And a brand-new team.</p>
<p>This team doesn’t exist on any org chart. It emerges organically, based on who knows whom.</p>
<p>And once you’ve been part of a team like that, something else happens: the next time finance hits a snag, your name will be the first one they remember.</p>
<p>Communication patterns don’t just influence software design; they determine how teams themselves are formed.</p>
<h2 id="the-strength-of-weak-ties">The strength of weak ties</h2>
<p>


  <img src="/images/2025-08-21-conway/ties.png" alt="A graph overview of connections some in weak ties others in strong ties" />

</p>
<p>Sociologist Mark Granovetter described these casual, cross-cutting relationships as <em>weak ties</em>. In his seminal article, <em>The Strength of Weak Ties</em>, he distinguished between two types of connections:</p>
<p>Strong ties are the ones you lean on every day: your close friends, the teammates you talk with constantly. They’re steady, reliable, always in your orbit.</p>
<p>Weak ties are different. They’re the people you see now and then, the friend of a friend, the guy you bump into while waiting for coffee. They’re lighter threads, but they reach into places your strong ties can’t.</p>
<p>Your strong ties generally talk about issues and projects where you are also part of, your weak ties have an entire different world of projects. Where everyone in your strong tie group might have a strong opinion on Rust Vs Go, your weak tie connection might not know what an IDE is.</p>
<p>Here’s why it matters. If Conway’s Law says products mirror the way people communicate, then it’s these weak ties that organize these connections. They spark the ideas, highlight inefficiencies, uncover opportunities hidden inside silos.</p>
<p>And when those casual chats turn into projects, weak ties evolve into strong ones. That’s when the team forms, the solution takes shape, and Conway’s Law writes itself into code and process.</p>
<h2 id="is-this-a-hidden-pitch-for-back-to-the-office-work">Is this a hidden pitch for back to the office work?</h2>
<p>One of the best communicated teams I’ve ever worked on was scattered across the globe. Most of us had never met in person, maybe once a year, if that. And yet it worked. It worked beautifully.</p>
<p>The secret was communication, plain and simple. And the right tools to make it effortless. For us, that tool was Slack.</p>
<p>Slack wasn’t just for projects. There were channels for music, hobbies, random banter. You’d stumble into a debate about The Beatles versus The Beach Boys, and suddenly you were talking to someone you might never have met in your formal service line. Those side doors opened up connections that resulted in ad-hoc projects and innovations.</p>
<p>This design matters. I’ve consistently seen Slack foster such informal networks, but I’ve rarely observed the same in Microsoft Teams. Its structure tends to constrain communication, which, according to Conway’s Law, suggests something about Microsoft’s own internal culture.</p>
<p>That said, tools have limits. Large enterprises with thousands of employees cannot easily replicate the intimacy of hobby-based channels. And personal preference plays a role as well: I value face-to-face interaction, though I recognize that not everyone does.</p>
<h2 id="the-architecture-you-cant-draw">The architecture you can’t draw</h2>
<p>The big problem about all of this theory to my architectural mind is that you can’t formalize this stuff.</p>
<p>There’s no neat diagram that captures weak ties or coffee-corner alliances. But you can encourage it. You can make room for it to happen.</p>
<p>That’s part of the reason<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> so many companies push for people to come back into the office, to spark those casual conversations again. And sure, there’s some truth in that. But let’s not forget the remote world. The tools we use every day shape how we connect.</p>
<p>Too often, they’re treated as an afterthought. Microsoft Teams may come bundled with your license, but convenience comes at a cost: missed connections, missed ideas, missed weak ties. And in those gaps, you lose opportunities that might have made all the difference.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I wrote more about this stuff, and how I don&rsquo;t really like it here: <a href="https://frederickvanbrabant.com/blog/2024-07-12-business-capabilities-how-i-like-to-use-them/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2024-07-12-business-capabilities-how-i-like-to-use-them/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>There are also a lot of other reasons, some of them are not as positive&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Pace layering an application portfolio</title><link>https://frederickvanbrabant.com/blog/2025-08-10-pace-layering-an-application-portfolio/</link><pubDate>Wed, 06 Aug 2025 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2025-08-10-pace-layering-an-application-portfolio/</guid><description>&lt;p&gt;In every organization I’ve worked with, there’s always been a handful of core applications, central, timeworn systems that quietly hold the business together.&lt;/p&gt;
&lt;p&gt;Knowledge of these systems often lives in the heads of a few long-tenured experts and is usually passed on through informal, almost ritualistic projects where newer employees are slowly initiated into their mysteries.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>In every organization I’ve worked with, there’s always been a handful of core applications, central, timeworn systems that quietly hold the business together.</p>
<p>Knowledge of these systems often lives in the heads of a few long-tenured experts and is usually passed on through informal, almost ritualistic projects where newer employees are slowly initiated into their mysteries.</p>
<p>These applications are typically the usual suspects: ERP<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> or SAP-like systems, but rarely are they clearly defined. Most teams just refer to them vaguely as “those old central apps.” That’s understandable: every organization is different, and rigid definitions are hard to nail down.</p>
<p>That&rsquo;s a bit of a shame. If we could define what makes these applications what they are, we can start to identify more of them, group them, and build some processes and governance around them. Make it all more manageable. That’s where Pace Layering comes in.</p>
<h2 id="the-three-layers-explained">The three layers explained</h2>
<p>Pace Layering or, as our friends at <a href="https://www.gartner.com/en"




 target="_blank"
 


>Gartner</a> call it: Gartner&rsquo;s PACE Layered Application Strategy is a strategic model for categorizing business applications based on their rate of change and purpose.</p>
<p>It breaks down the portfolio into three distinct layers: <strong>systems of record</strong>, <strong>systems of differentiation</strong> and <strong>systems of innovation</strong>.</p>
<p>


  <img src="/images/2025-08-06-pace/PaceOverview.png" alt="A schema showing a breakdown of the layers, the SOR is the center one, SOD around it and the most outer layer is the SOI" />

</p>
<h3 id="system-of-record-sor">System of Record (SOR)</h3>
<p>These are the foundational platforms: your ERP things, CRMs structures, and core finance system monsters that support your primary business processes. They’re stable, slow-moving, and represent the source of truth for the organization. They don’t move quickly. Nor should they.</p>
<p>There might even be full teams dedicated to keeping these applications running. They are typically internally facing and are the source of analytics and reports in the organization.</p>
<h3 id="system-of-differentiation-sod">System of Differentiation (SOD)</h3>
<p>These are the bulk of your applications, the ones that fill the gaps and target specific business needs, market nuances, regional regulations, customer expectations, and the likes.</p>
<p>Replacing these takes planning, but is something that happens more or less often. Typically, the applications have shared teams that manage multiple of these kinds of applications. They connect upstream and downstream with both SORs and SOIs.</p>
<h3 id="system-of-innovation-soi">System of Innovation (SOI)</h3>
<p>This is the R&amp;D part of the portfolio. Think prototypes, MVPs, R&amp;D applications. Experiments designed to test value and adaptability. The development cycles are short, the risk tolerance is higher, and successful projects often graduate to SOD status.</p>
<h2 id="the-valuable-part">The valuable part</h2>
<p>Simply classifying your applications using pace layering is already a step up from what most organizations do. It forces teams to look at each system and ask: what role does this serve, and how fast does it need to move?</p>
<p>But here’s where the real power lies: once you know what layer something belongs to, you can start treating it the way it wants to be treated.</p>
<p>What I mean by that is: We want our System of Records as stable as possible so we want to implement rigid rules about documentation and change management. Yet at the same time we don&rsquo;t want to slow down our innovation, so we apply looser restrictions on our innovation layer. We only start our deeper documentation on SOI applications once they have proven their worth and move up to the SOD layer.</p>
<p>For <strong>Systems of Record</strong>, you build walls. High ones. Movement is deliberate, documented, approved. These are the systems you defend at all costs.</p>
<p><strong>Systems of Differentiation</strong> are the middle child, they get more responsive governance. They must be able to evolve, often in collaboration with business stakeholders, while still respecting integration constraints.</p>
<p>The <strong>Innovation layer</strong>? Let it run barefoot. Don’t smother it with process, give it space to grow wild. You’ll prune later.
Just make sure it doesn&rsquo;t step barefoot in a data breach or compliance audit.</p>
<p>This layered governance model avoids a one-size-fits-all approach. It enables agility at the edges while reinforcing stability at the core.</p>
<h3 id="making-it-multi-tenant">Making it multi tenant</h3>
<p>If our organization has multiple tenants (think a multinational) we could centrally host and manage all of our system of record applications in the global organization.</p>
<p>This would bring standardization across all the member firms of the organization. Every sub company can rely on the core applications to do their jobs without having to duplicate work.</p>
<p>In exchange, this central company would be able to have standardized reporting and data to compare region and comply to global rules.</p>
<p>The system of differentiation applications could then augment the SOR processes towards the local markets. This is what the member firms themselves would manage and are different per member firm/country. This gives us a centralized way of working while still being open to market trends and member firm sizes.</p>
<h3 id="funding">Funding</h3>
<p>This is obviously very organizational depended, but rules towards funding might also be easier to define.</p>
<p>In a multi-tenant setup, central governance can take charge of Systems of Record, just like SaaS subscriptions, giving member firms scale, lower costs and not having to plan the big slow projects.</p>
<p>Said member firms could host and licence their own differentiation stack and invest together from a central innovation budget pool into the System of Innovation applications. This makes sure that there is always budget for innovation and ensuring that innovation flows back into the member firms.</p>
<p>In a single-company model, the same architecture fits: central IT handles the stable Systems of Record, business units drive differentiation tools, and shared innovation emerges through collective investment.</p>
<h3 id="change-management-and-projects">Change management and projects</h3>
<p>Change management benefits from the same structural clarity.</p>
<p>As we have a very rigid foundation we ideally want to keep it that way. This makes it a perfect candidate for waterfall delivery: detailed planning up front, followed by structured, well-documented rollouts.</p>
<p>The SOD and SOI project can move forwards in an Agile way and, in the case of SOI projects, we could even move towards <a href="https://en.wikipedia.org/wiki/Extreme_programming"




 target="_blank"
 


>Extreme Programming</a>.</p>
<p>Each layer gets the methodology it deserves, aligned to its purpose and risk profile.</p>
<p>


  <img src="/images/2025-08-06-pace/table.png" alt="Most of the information above in a table" />

</p>
<h2 id="theory-of-constraint">Theory of constraint</h2>
<p>One of the most powerful ideas in systems thinking is the <strong>Theory of Constraints</strong>, developed by Dr. Eliyahu Goldratt. The core principle is simple: every system has at least one bottleneck: one limiting factor that restricts the performance of the entire system. If you want to meaningfully improve the system’s output, you need to improve the most limiting constraint.</p>
<p>If you’ve played <a href="https://www.factorio.com/"




 target="_blank"
 


>Factorio</a>, you’re already nodding. That one slow assembler choking your production line? That’s your constraint. If you haven’t played it<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, imagine a factory where one machine lags behind the rest: that’s where your focus should go. Optimize that, and the whole system improves to the limiting factor of the next machine&rsquo;s constraint.</p>
<p>In the context of business applications, we’re talking about the performance of your entire application portfolio. And the constraint is almost always lurking in the System of Record layer.</p>
<p>SORs are the bedrock: customer data, financial records, contracts, inventories. Everything else depends on them. Differentiation and Innovation systems are build on top of that layer. So when the SOR layer is slow, outdated, or fragile, it limits <em>everything</em> above it.</p>
<p>In multi-tenant organizations, the situation is even more amplified. The same SORs are shared by multiple business units or firms.</p>
<p>Every improvement to that foundational layer, through decoupling, caching, data replication, … has a cascading, multiplicative benefit across the enterprise. Small improvements here can mean millions of euros.</p>
<p>Of course, this doesn’t mean SOD or SOI layers are constraint-free. They still need love, care, and focus. But if you’re looking to accelerate the system as a whole, the SOR is the first place to look.</p>
<p>


  <img src="/images/2025-08-06-pace/contraint.png" alt="A schema showing one system that is slower and making all connections to said system also slower" />

</p>
<h2 id="moving-through-maturity">Moving through maturity</h2>
<p>Applications don’t stay static. As they prove their value, they can shift layers.</p>
<p>A System of Innovation might start as a scrappy experiment. If it sticks, if it delivers clear value, it can evolve directly into a System of Differentiation, or even a System of Record. The same goes for Differentiation systems. Once they become critical enough, they can be formalized, hardened, and moved into the SOR layer.</p>
<p>But let’s be clear: <strong>this isn’t a promotion.</strong></p>
<p>The goal of an application isn’t to climb the maturity ladder. Each layer plays a different role in your portfolio, and the real objective is having the right tools for the right roles in an organization.</p>
<p>Yes, Innovation projects shouldn’t linger in limbo for years. At some point, you decide: invest further or shut them down. But a Differentiation system? That can sit comfortably in its layer for a decade. As long as it’s still giving your organization a competitive edge.</p>
<h2 id="moving-on-from-here">Moving on from here</h2>
<p>There is a lot of value here, and everything described above is just the beginning. Once you’ve applied pace layering, you open up all kinds of extra insights.</p>
<p>You can link your applications to your <a href="https://frederickvanbrabant.com/blog/2024-07-12-business-capabilities-how-i-like-to-use-them/"




 target="_blank"
 


>business capabilities</a> and ask: are the tools you consider differentiating actually supporting the areas where you aim to differentiate?</p>
<p>You can look at <a href="https://frederickvanbrabant.com/blog/2025-06-27-the-cost-of-ownership-of-a-1000-applications/"




 target="_blank"
 


>total cost of ownership</a>. Are your innovations worth the money? Are your differentiators driving up costs without payoff?</p>
<p>And we haven&rsquo;t even talked about <a href="https://frederickvanbrabant.com/blog/2025-02-05-modeling-data-and-information-in-an-organization/"




 target="_blank"
 


>data modeling </a>! Are you systems of record actually holding the data they claim to hold or is critical information quietly hiding in an innovation project or a differentiation app?</p>
<p>All of that can come later.</p>
<p>Even if you just go through your portfolio and start labelling what sits where, even just <strong>naming</strong> your Systems of Record, you’re already ahead of the curve.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Enterprise Resource Planning&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Don&rsquo;t go looking it up if you enjoy evenings away from a computer&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>The real ask</title><link>https://frederickvanbrabant.com/blog/2025-07-22-the-real-ask/</link><pubDate>Sun, 27 Jul 2025 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2025-07-22-the-real-ask/</guid><description>&lt;p&gt;One of my mentors, &lt;a href="https://www.linkedin.com/in/stevencaus/"
target="_blank"
&gt;Steven Caus&lt;/a&gt;, always taught me the concept of “the question behind the question”: The question we receive is not always the problem we need to solve.&lt;/p&gt;
&lt;p&gt;The concept is very easy. When someone comes to you with a question to do something, instead of blindly doing the ask, take a step back and try to understand what they actually want to achieve. Often this task might actually not be the best way to achieve the goal they are set out to do.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>One of my mentors, <a href="https://www.linkedin.com/in/stevencaus/"




 target="_blank"
 


>Steven Caus</a>, always taught me the concept of “the question behind the question”: The question we receive is not always the problem we need to solve.</p>
<p>The concept is very easy. When someone comes to you with a question to do something, instead of blindly doing the ask, take a step back and try to understand what they actually want to achieve. Often this task might actually not be the best way to achieve the goal they are set out to do.</p>
<h2 id="what-is-the-real-ask">What Is the Real Ask?</h2>
<p>So let&rsquo;s start with an example story.</p>
<p>A project team comes to you with the ask for a new project management tool. They&rsquo;d already narrowed it down to three shiny options, each with slick demos and long feature lists. All they want is your take: which one fits best with our architecture and strategy?</p>
<p>Before you dive into the selection process, you ask: “What’s wrong with the one you have now?”</p>
<p>Their answer comes fast: “It’s old, it’s slow, and everything is manual. We’re already drowning, this tool is just dragging us down further.”</p>
<p>In this case, the tool is not really the issue. The real issue is that the team is overworked and/or understaffed for the current workload. Sure, the tool might be old and slow, but the real pain point is not going to go away with a new shiny tool. In fact, new tools bring migrations. Setups. Training. And none of that helps when you&rsquo;re already gasping for air.</p>
<p>Instead, we can look upstream: Could we ease the pressure on their pipeline? Automate the worst manual steps? Give them breathing room without introducing a six-month, and figure, rollout?</p>
<p>They won’t be thrilled at first. No shiny new toy. But we saved serious money, dodged a risky project, and actually helped fix the <em>real</em> problem.</p>
<p>All because we paused to ask the question behind the question.</p>
<h2 id="why-does-this-happen">Why does this happen?</h2>
<p>We as architects have the benefit of a bird&rsquo;s eye vision of an organization. This is a luxury that not a lot of teams have. We sit where we can trace lines across systems and spot patterns that others might miss.</p>
<p>But the project team in our story? They were deep in the reeds, overworked, short on time, and grinding through manual tasks that drained their energy. From their vantage point, the tool looked like the enemy. And who could blame them? When you&rsquo;re underwater, you sometimes dream of better scuba gear instead of the shore.</p>
<p>These conversations aren’t about proving someone wrong. They&rsquo;re about <strong>collaboration</strong>. You’re not trying to be the smartest guy in the room, you’re trying to help. They know the grind, the details, the day-to-day. You bring the context and the different lens.</p>
<p>They might not be aware of what&rsquo;s possible or what other teams are doing, and you are not aware of the processes they are using.</p>
<p>The question behind the question isn’t a challenge. It’s a sanity check. A way to align intent with action.</p>
<h2 id="why-architects-keep-falling-for-the-trap">Why architects keep falling for the trap</h2>
<p>We all want to be helpful, especially to a team that seems to be struggling. If they come to you with what looks like a silver-bullet solution, it’s hard to say no.</p>
<p>Telling them they can’t have the new tool, or that their idea might not fix the real issue, feels like letting them down.</p>
<p>It is, however, your job. </p>
<p>Our role isn’t to rubber-stamp requests. It’s to protect the long-term health of the organization. And that means recognizing when a “solution” might actually add more weight to a system that’s already strained.</p>
<p>A quick fix that doesn’t solve the root cause becomes <strong>baggage</strong>. And that baggage sticks around for years.</p>
<p>Do the hard thing now, so you don’t have to dig yourself out later.</p>
<h2 id="how-to-surface-the-question-behind-the-question">How to surface the question behind the question.</h2>
<p>Getting to the real ask takes finesse.</p>
<p>If you come in swinging with, <em>“What are you really trying to achieve?”</em>, people get defensive. It sounds like a challenge. Like you’re poking holes in their thinking.</p>
<p>Same goes for, <em>“It sounds like what you actually want is…”</em> That reads as smug, even if you mean well.</p>
<p>So take a different tack. Ask about their pain points. What’s frustrating? What’s taking time? What feels like it should be easier?</p>
<p>Look for patterns. Oddities. Things that don’t line up. Compare their setup with how other teams work. Look at the rest of the organization.</p>
<p>And when you’ve gathered enough threads, step back and hold them up. _“So the tool’s slow and the work is tedious, and you&rsquo;re already stretched thin. Would switching tools really fix that, or just add another burden?”</p>
<p>It’s a little like a detective story. You&rsquo;re not here to interrogate. You&rsquo;re here to investigate. To ask the quiet questions that lead to real answers.</p>
<p>And you’re not doing it alone. You’re on the same side of the table. Solving the same mystery.</p>
<h2 id="beneath-the-surface">Beneath the Surface</h2>
<p>People don’t always ask for what they <em>really</em> need. They ask for what they think will stop the bleeding as fast as possible. That’s human.</p>
<p>Your job is to listen, poke gently at the edges, and find the story underneath… That’s what separates an architect from an administrator.</p>
<p>Sometimes that means you’ll disappoint them in the short term. But if you’ve done your job right, you&rsquo;ll be solving real issues and not introduce future ones. Because in architecture, the easy answer rarely ages well.</p>
]]></content:encoded></item><item><title>Chesterton’s Fence and paralysing your organization</title><link>https://frederickvanbrabant.com/blog/2025-07-11-chestertons-fence-and-paralysing-your-organization/</link><pubDate>Sun, 13 Jul 2025 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2025-07-11-chestertons-fence-and-paralysing-your-organization/</guid><description>&lt;p&gt;Some years ago I worked at a place that had, buried deep in the codebase, a service running that combed through the central data warehouse and flagged certain users. One through seven, except four.&lt;/p&gt;
&lt;p&gt;A left over from an old proof-of-concept application that had something to do with GDPR. This field went out over the API as part of the “employee data” resource.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>Some years ago I worked at a place that had, buried deep in the codebase, a service running that combed through the central data warehouse and flagged certain users. One through seven, except four.</p>
<p>A left over from an old proof-of-concept application that had something to do with GDPR. This field went out over the API as part of the “employee data” resource.</p>
<p>Now we all agreed that it&rsquo;s probably not used, but it was still bundled into the “employee data” resource that went out to the rest of the application landscape. There was no way to know if it was unused, or if something obscure still depended on it.</p>
<p>I&rsquo;m sure it&rsquo;s still running to this day. Every morning, at 3 AM. </p>
<h2 id="chestertons-fence">Chesterton’s Fence</h2>
<p>That forgotten flagging system? That&rsquo;s a direct result of Chesterton’s Fence, a principle by G.K. Chesterton, that states that you should only remove something if you truly understand why it was there in the first place.</p>
<p>In architecture this is often spoken as gospel, but architects also often forgot to act on the second part.</p>
<p>Chesterton&rsquo;s fence is frequently used in some political circles and think tanks, but we are not going there. This post is not about politics; we are only looking at it from a business and organizational perspective. Like a smart man once said: “I&rsquo;m not brave enough for politics”.</p>
<h2 id="how-this-works-in-architecture">How this works in architecture</h2>
<p>A significant aspect of architecture is following up on vague leads.</p>
<p>A team says, “We don’t use that onboarding system any more.”
You ask, “Are you sure?”
And then you check. You check dependencies, integrations, the undocumented mess that holds the whole thing together.</p>
<p>We are now deep into Chesterton’s Fence, it’s about knowing what happens <em>after</em> you remove something. Not the removing itself.</p>
<p>But sometimes, that double-check turns into a rabbit hole.</p>
<p>You dig through missing docs. You talk to five teams. Three have left. Two don’t remember. Eventually, new projects pop up. Everything is always critical so you stop digging.</p>
<p>And the onboarding system fence stays. What started with good intentions into due diligence, quickly turns into risk avoidance and bloated systems. </p>
<h2 id="fences-around-fences">Fences around fences</h2>
<p>In reality, what often happens is that teams build around these mysterious setups.</p>
<p>Remember that flagging system from the intro? What happens when that same company needs a new user flag? They don’t touch the old one. No one remembers what it does. That&rsquo;s irresponsible! So they add a new field to the database.</p>
<p>Now every developer must guess which flag to use. One that might result in an error two systems further, or the one that actually works.</p>
<p>Down the line some developers will guess wrongly, slowing down feature development and increasing the risk of bugs. </p>
<p>Scale that same issue up over ten years, and you have slowed down all your projects and innovation. Simple changes take six months, as there is just too much complexity to deal with.</p>
<h2 id="this-is-not-a-risk-problem-this-is-a-knowledge-retention-problem">This is not a risk problem; this is a knowledge retention problem</h2>
<p>The real fear isn’t the fence, it’s the surrounding silence.</p>
<p>At some point, there was a very good reason to build the system like it is today. Nobody wrote it down, that person left and the system stayed.</p>
<p>Organizations know the value of documentation, yet there is often no time or room to write it. This is a self-amplifying problem where projects run over budget cause there is no documentation, and therefore everything takes too long to figure out. But at the same time there is no room to write documentation as the project is already overdue.</p>
<p>If you run into applications or processes that nobody really knows what they do, it&rsquo;s already way too late. Doing a deep dive into the inner workings of the system will be a huge undertaking and one that probably doesn&rsquo;t fit in the current timeline.</p>
<p>This should, however, be a wake-up call where you start to invest more in knowledge sharing platforms, process mappings and application/business owners of processes and technology.</p>
<p>Organizations forget. And the cost of that forgetting is paralysis.</p>
<h2 id="so-should-we-follow-it-or-not">So, should we follow it or not?</h2>
<p>Chesterton was right: don’t tear down what you don’t understand. But in big organizations, no one tears things down. They build around them. Layer upon layer. Fence upon fence, until you’ve built a maze of fences no one can navigate.</p>
<p>The solution is a centralized knowledge system that actually captures information in your organization and makes time for people to own, read and write said documentation. Because if you don’t, every new project will have to tiptoe around yesteryears old mysterious baggage.</p>
<p>So yes, be cautious. But don’t be afraid to take a risk now and then.</p>
<p>Worst case, you break something.
Best case, you find out what that fence was for.
And maybe, just maybe, you can finally tear it down.</p>
]]></content:encoded></item><item><title>The cost of ownership of a 1000 applications</title><link>https://frederickvanbrabant.com/blog/2025-06-27-the-cost-of-ownership-of-a-1000-applications/</link><pubDate>Sun, 29 Jun 2025 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2025-06-27-the-cost-of-ownership-of-a-1000-applications/</guid><description>&lt;p&gt;Cost reduction is one of the main focuses of so many companies out there today. The market is not great, and that is the moment companies take a deep look at the financials of it all.&lt;/p&gt;
&lt;p&gt;One of the first things that is being asked is: What are we really spending?&lt;/p&gt;
&lt;p&gt;Not just the big hard numbers like contracts and licences, but everything. The hidden costs and invisible hours.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>Cost reduction is one of the main focuses of so many companies out there today. The market is not great, and that is the moment companies take a deep look at the financials of it all.</p>
<p>One of the first things that is being asked is: What are we really spending?</p>
<p>Not just the big hard numbers like contracts and licences, but everything. The hidden costs and invisible hours.</p>
<p>This is weirdly something not a lot of companies know. Today, we will take a look at just that: TCO (total cost of ownership), and how you can start mapping it across your entire application landscape.</p>
<h2 id="the-outline">The outline.</h2>
<p>The idea is simple: take every business application in your landscape, break its costs into categories, and combine them into a single number. Or, if numbers aren’t available, a t-shirt size. Simple enough. Now do it for a thousand applications.</p>
<p>But things get messy when we actually start the exercise.</p>
<p>Total Cost of Ownership feels like a clear, manageable concept, until you try to measure it. What begins with server costs quickly spirals. What about storage? Backups? Do you include electricity, if you’re hosting in-house<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>? And that&rsquo;s just the surface of it all.</p>
<p>There is also the cost of building, maintaining, training, … I’m sure you can think of dozens more parameters.</p>
<p>Remember, we’re doing this at scale. Every extra detail you include means another person to ask, another number to update, another layer to manage.</p>
<p>But here’s the trade-off: too little data, and your TCO becomes useless. You want something clear. Something that tells the story of the cost.</p>
<p>For example: Application X costs 500,000 per year. You break it down: 180K on infrastructure and another 100K on training. Suddenly, you’re not just seeing a total cost. You’re seeing an opportunity. Maybe that training cost tells you it’s time to invest in better UX.</p>
<p>That’s what this is really about: not just knowing the number, but knowing <em>how to change it</em>.</p>
<p>For me, the best way to go about this is to map this out in categories, and the ones I&rsquo;ve settled on are: </p>
<ul>
<li>Development</li>
<li>Licensing</li>
<li>Infrastructure</li>
<li>Support &amp; Maintenance</li>
<li>End-user Support</li>
<li>Misc</li>
</ul>
<p>


  <img src="/images/2025-06-27-tco/TCO1.drawio.png" alt="A schema showing a breakdown of the categories that result in the endsum" />

</p>
<p>I won’t go through every detail of every category, this isn’t a textbook, and every organization has its own quirks. I will however give you a rough outline that you can use to shape it to your needs.</p>
<h3 id="development">Development</h3>
<p>This category covers in-house development.<br>
If you’ve built the application yourself, take the total cost: FTE&rsquo;s <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, tools, consulting, whatever it took to make the thing. And spread it across five years.</p>
<p>Why five? Because without that, you get a big spike in year one that skews everything. You want apples to apples.</p>
<p>If the application is off-the-shelf or SaaS, this category will likely sit at zero. And that’s okay. Not every column needs to be filled.</p>
<h3 id="licensing">Licensing</h3>
<p>This is your “buy” column: SaaS, commercial software, vendor tools…</p>
<p>Licensing can be deceptively complex. You’ve got per-seat pricing, per-admin models, resource usage tiers… I always normalize it. I use “cost per unit” as a base metric. It helps highlight inefficiencies fast.</p>
<p>For example, if someone only reads reports, maybe they don’t need that premium admin licence. Trim where it makes sense.</p>
<h3 id="infrastructure">Infrastructure</h3>
<p>This covers servers, storage, backups: the whole physical and virtual stack.</p>
<p>In some cases, infrastructure is bundled into licensing (think cloud platforms). If that’s true, you can merge them. If not, keep them separate. Just be consistent.</p>
<p>What matters most is visibility: where the app lives, and what it costs to keep it breathing.</p>
<h3 id="support--maintenance">Support &amp; Maintenance</h3>
<p>Now we’re into the hard stuff: the cost of keeping things running. Bug fixes. Patches. Support tickets.</p>
<p>Some companies calculate this by assigning a cost per bug. That works, but I prefer a more stable approach: support hours, tracked in FTEs. This will deviate less per month and doesn&rsquo;t feel like punishment to the people working there.</p>
<p>Here’s how I break it down.<br>
Two support engineers cover five applications? That’s 2 ÷ 5 = 0.4. Round it up to 0.5 FTE per app. (I always move in steps of 0.5, there is no need for more details I feel)</p>
<p>If that application constantly escalates bugs to the dev team, bump the number.</p>
<p>It’s not perfect, but it gives you something consistent to track.</p>
<h3 id="end-user-support">End-user Support</h3>
<p>This one’s slippery. It covers training and minor feature requests. The human side of keeping an application usable.</p>
<p>Track training in FTEs. Not just the students, but whoever’s doing the teaching. That includes onboarding, train-the-trainer sessions, and refresher courses.</p>
<p>As for feature requests: draw a line:</p>
<ul>
<li>Big, roadmap-changing features go in Development.</li>
<li>Small asks, tweaks, quality-of-life fixes go here.</li>
</ul>
<p>And if you rely on a vendor for this kind of support? Log it here, not under licensing. That way, you can spot where the support load actually lives.</p>
<h3 id="misc">Misc</h3>
<p>The vaguest one, but an important one. This is where I track three things: <strong>criticality</strong>, <strong>sensitivity</strong>, and <strong>legacy</strong>. I express each one as a percentage.</p>
<p>Is it business-critical? Does it handle sensitive data? Is it built on something ancient and fragile? The higher the percentage, the more weight it carries.</p>
<p>Yes, I know, that&rsquo;s very unscientific and vague, but I do think it&rsquo;s essential. We&rsquo;re going to use these in the next step.</p>
<h2 id="the-thing-about-numbers">The thing about numbers…</h2>
<p>Numbers are sensitive and not always easy to get hold of. And when you’re dealing with this many systems, they don’t always show up when you need them.</p>
<p>Not only that, but as you probably noticed in the previous chapter, we mix percentages, FTEs, exact figures. It might seem messy, but that’s the only way to capture something so layered.</p>
<p>You have to realize that the end number you&rsquo;re going to end up with is not accurate. It is impossible to get a real accurate number out of this. There’s too much guesswork, too many estimates, too many people giving you ballparks instead of precision. That’s reality.</p>
<p>Don’t chase perfection. You won’t find it.</p>
<p>So give people options. If they know the cost, great. Write it down. If they don’t, let them give you a T-shirt size: S, M, L, XL, XXL. It’s fast, and surprisingly helpful at scale.</p>
<p>


  <img src="/images/2025-06-27-tco/TCO4.drawio.png" alt="An example of a fake Excel row that shows an application that has low bugs but isn&rsquo;t tracked so is assigned a t-shirt size" />

</p>
<p>This helps if you run into situations where people just can&rsquo;t answer you with real data. How many days a month do you spend on average on bug squashing? If you don&rsquo;t have a clear setup to track these things, it would be impossible to know.</p>
<p>Remember: you’re doing this for 1,000 applications. Progress beats precision. Every estimate you get is better than one more week waiting on the “real” number.</p>
<h2 id="getting-to-the-final-number">Getting to the final number</h2>
<p>Now comes the tricky part: turning estimates into meaning.</p>
<p>Start by defining your defaults. What does a “Medium” look like for bug reports per month? Feature requests? These won’t be perfect. That’s okay. Set some gut-based baselines and refine them as you go. An “M” for some companies can be a “S” for others they can and even will change over time. Don&rsquo;t be afraid to revisit them later on.</p>
<p>The same with the FTE calculations, that&rsquo;s the cost per day of a person working in that position. Again, an average.</p>
<p>Once those are locked in, total up your categories. You’ll have a rough annual cost per application.</p>
<p>Now, what about that <strong>Misc</strong> tab with the percentages? Those percentages: <strong>criticality</strong>, <strong>sensitivity</strong> and <strong>legacy</strong> should affect the rest. Not by simple addition. You’ll need to apply a weighting system to the total cost. A legacy app doesn’t just cost money it also costs effort.</p>
<p>It&rsquo;s up to you how you want to weigh these multipliers. I would not just add the percentage to the number, as that would quickly get out of hand. Instead, you might want to use a lighter touch. It&rsquo;s best to experiment here.</p>
<p>


  <img src="/images/2025-06-27-tco/TCO2.drawio.png" alt="A schema showing a breakdown of the categories that result in the endsum with a modifier applied" />

</p>
<h2 id="scaling-it-up">Scaling it up</h2>
<p>Once your model’s ready, it’s time to gather data. Keep the entry points soft and friendly. A well-designed form, a guided tool, a low-code application with just the right help text. These things matter.</p>
<p>Don’t overload anyone. Break your model into pieces, and send each one to the person best suited to answer it. Let Finance handle licensing. Let Ops tackle infrastructure. Let people speak in their own language. The last thing you want is to scare people with information they know nothing about.</p>
<p>


  <img src="/images/2025-06-27-tco/TCO3.drawio.png" alt="A  schema showing a breakdown of the categories that result in the endsum with a modifier applied and assigned to different teams" />

</p>
<p>And whatever you do, keep the calculations hidden. Not to be secretive, but to protect the integrity of the answers. If people can see how the sausage is made, they’ll start shaping the meat.</p>
<p>Once you have everything set up, it&rsquo;s selecting the right people and sending out the forms.</p>
<p>Don’t expect instant returns. You’re building momentum, not flipping a switch.</p>
<p>Try to do it in small sections and run a POC first, where you only scope out a 20. That way, you can see if you have some faults in your system. Before you open it up to maybe batches of 50 to 100&rsquo;s.</p>
<p>The end goal is for people to come to you with the information so you don&rsquo;t have to chase them every few years. That will only happen if your insights at the end of the project give enough value and reach the right people.</p>
<p>So, make sure that you also present this information with some actionable insights. That should be very doable with a setup like this.</p>
<p>TCO is messy, imperfect, and often imprecise, but so is everything worth understanding. Start somewhere. Adjust as you go. Just don’t wait for perfect.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Is this a variable summer/winter rate?&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Full-Time Equivalent. The people who worked full-time, and in this case, the cost of that.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Choosing where to spend my team’s effort</title><link>https://frederickvanbrabant.com/blog/2025-06-13-choosing-where-to-spend-my-teams-effort/</link><pubDate>Sun, 15 Jun 2025 12:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2025-06-13-choosing-where-to-spend-my-teams-effort/</guid><description>&lt;p&gt;It’s the start of a new fiscal year. Strategy season. That time when all the grand ideas come out and everyone is still hopefull.&lt;/p&gt;
&lt;p&gt;Over the years, I’ve settled into a structure that helps me define projects that not only link to the strategy above but also looks at my own team&amp;rsquo;s enviroment, I thought I&amp;rsquo;d share it here.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>It’s the start of a new fiscal year. Strategy season. That time when all the grand ideas come out and everyone is still hopefull.</p>
<p>Over the years, I’ve settled into a structure that helps me define projects that not only link to the strategy above but also looks at my own team&rsquo;s enviroment, I thought I&rsquo;d share it here.</p>
<h2 id="working-with-the-given-strategy">Working with the given strategy</h2>
<p>Strategy usually comes down from the top. The C-suite sketches the big picture, department heads slice it into projects, and those trickle down to the teams. It&rsquo;s not always a very transparent process, but that&rsquo;s how it works.</p>
<p>Knowing this, there are some ways you can take (partly) ownership of this process by injecting your own projects into it. We&rsquo;ve talked a lot on this site about organizational layers<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> so there might be some projects that you can think of that benefit the strategy that layers above you might not see. What you see depends on where you sit. That means there might be gaps in vision, gaps that you can fill.</p>
<p>First things first is getting to know the strategy. That sounds extremely obvious, but it&rsquo;s something, so many team leads skip over. Having a conversation with your boss about where the company wants to go towards in the next X years and where your boss&rsquo;s boss wants to go, and why. The bigger picture stuff. If you&rsquo;ve never done that, you&rsquo;d be surprised how suddenly some questionable decisions from the past suddenly make a lot more sense. This is something I would do at least yearly.</p>
<p>All the work will be routed in this. If you can pitch your projects in the framework of the company&rsquo;s strategy your odds of a successful pitch will skyrocket, most of the convincing has been done for you already. Not only that, your boss can easily pitch that project to the higher ups. Everyone wins.</p>
<h2 id="converging-strategy-into-projects">Converging strategy into projects</h2>
<p>Ok, you’ve got the strategy. Now how do you turn that into projects? I like to use a bastardization of Teresa Torres&rsquo;s opportunity solution tree. It’s a startup thing, but it works just fine in the grand halls of enterprise.</p>
<p>I use it like this<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>:</p>
<p>You take one of the strategies. An example would be: we want to reduce cost. The next step is to start linking broad actions that we could take to handle that strategy. EG: automate processes, eliminate underused software, look at licences. Don’t judge the ideas too much yet, this is the part where you make the mess.</p>
<p>You can do of these things alone, but it&rsquo;s probably better to get some smart people in the room from your team. Again, you only know what you know. More brains, more angles.</p>
<p>Once we have these vague ideas, it&rsquo;s time to shift from vague to specific. It&rsquo;s possible that you can&rsquo;t find a real project for an item in the tree above, or that if you go deeper into it, it turns out that it was not a great idea. This is good, that means that you are already filtering out the fluff.</p>
<p>The last step is thinking of a proof of concept, this is another filter layer. What can we quickly build that proves this idea has legs? If you can&rsquo;t find something that can at least proof rather quickly if there is value or not, it&rsquo;s probably not something to pursue. This process turns abstract strategy into stuff that ships and also gives you the ammunition to pitch it, as you walked the logic all the way down.</p>
<p>You might end up with something like this <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>:</p>
<p>


  <img src="/images/2025-06-13-ost/OPT1.drawio.png" alt="A schema outlining the strategy to idea to project" />

</p>
<h2 id="this-is-good-but-it-can-be-better">This is good, but it can be better</h2>
<p>You could stop here and have a solid list of projects. Your boss would be impressed. But I think the real kicker would be to have projects that serve more than one strategy.</p>
<p>At the “vague idea” stage, before turning anything into a project, pause. Run the same exercise for your other strategies. Build separate trees.
Then take a step back. Look across them. You’ll start to see overlap, ideas that touch multiple goals at once.
Those are the winners. The projects that move more than one needle. The ones that get budget and visibility.</p>
<p>


  <img src="/images/2025-06-13-ost/OPT2.drawio.png" alt="A schema outlining the strategy to idea to project this time with multiple ideas linked to multiple strategies" />

</p>
<p>This is where the real value starts showing up.</p>
<p>Once you start defining projects, focus on the ones that serve multiple strategies at once. For example:</p>
<p>Say the sales team manually emails orders to the delivery team. You could replace that with an API connection. That alone cuts labor costs. But there’s a second win: if you’re also planning Project X, and it needs real-time order data, that API suddenly becomes critical infrastructure. One move, two wins.</p>
<p>So now your project becomes: <strong>“Build an API that connects sales and delivery across product platforms.”</strong></p>
<p>And when you kick off Project X, you’re already set up to automate and move faster.
You didn’t just deliver a feature. You moved strategy forward on two fronts.</p>
<h2 id="but-i-dont-have-the-freedom-to-define-my-teams-work">But I don&rsquo;t have the freedom to define my team&rsquo;s work</h2>
<p>Most teams don&rsquo;t. But this is a first step towards something like that. What we are doing here is helping your boss look good to their boss. We reach out and help find answers to the questions they get asked.</p>
<p>The first time you try, you might get brushed off, plans are already locked in, no time for new ideas. That’s fine. The second time the strategy to project planning comes around, you have a higher chance to be in the room where it happens, as you&rsquo;ve proven in the past that you understand how these things work.</p>
<p>And yes, your team is probably already busy. Everyone is. It is important to remember that you&rsquo;re reaching out with potential projects that we can do, ideally replacing assigned work with chosen work, not taking on the extra projects. Suggesting projects doesn’t mean you’re committing to them: it means you’re helping to shape the conversation.</p>
<p>Replace assigned work with chosen work. That’s the shift.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Examples here:</p>
<ul>
<li><a href="https://frederickvanbrabant.com/blog/2025-05-30-whats-the-role-of-software-in-an-oranization/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2025-05-30-whats-the-role-of-software-in-an-oranization/</a></li>
<li><a href="https://frederickvanbrabant.com/blog/2025-03-09-enterprise-architecture-skunk-works/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2025-03-09-enterprise-architecture-skunk-works/</a></li>
<li><a href="https://frederickvanbrabant.com/blog/2025-01-17-turning-complexity-into-manageable-complication/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2025-01-17-turning-complexity-into-manageable-complication/</a> (This one is the most important one in my opinion)</li>
</ul>
&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></li>
<li id="fn:2">
<p>Again, this is not how opportunity solution trees normally work, this is a bastardization of Teresa Torres&rsquo;s work. This is how I work in the vague context of the idea.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>This is a bit cross teams. That&rsquo;s not perse a bad thing, you could share this to the other teams and work together but ideally you focus on the things your team could do. The reason the example is so broad is to make it more accessible&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>What's the role of software in an organization</title><link>https://frederickvanbrabant.com/blog/2025-05-30-whats-the-role-of-software-in-an-oranization/</link><pubDate>Fri, 30 May 2025 00:00:00 +0000</pubDate><guid>https://frederickvanbrabant.com/blog/2025-05-30-whats-the-role-of-software-in-an-oranization/</guid><description>&lt;p&gt;Last week, while sitting in a bar, I had a chat with a good friend of mine about the role of software in an organization. I stated that the primary role software plays in an organization is to facilitate processes. My friend argued that this is a strict and overly theoretical view of software in the workplace. He might be right, but nevertheless, I think there is an article in this conversation.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>Last week, while sitting in a bar, I had a chat with a good friend of mine about the role of software in an organization. I stated that the primary role software plays in an organization is to facilitate processes. My friend argued that this is a strict and overly theoretical view of software in the workplace. He might be right, but nevertheless, I think there is an article in this conversation.</p>
<h2 id="applications-support-processes">Applications support processes</h2>
<p>An organization is a collection of people that come together and either provide a product or provide a service; that&rsquo;s the most zoomed-out version of an organization you can have. To provide this service or product (from now on just service, spares me keystrokes), you have your people, processes, technology and information <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. The people that do the work, the processes that the people follow to do the work, the technology that allows them to do the work and the information that is shared between all of the previous three.</p>
<p>You need all four to produce your service, but the leading one here is the process. That&rsquo;s the one that provides the blueprint of the work to be done. That means that the people and technology in this context support the process.</p>
<p>You build or buy new technology to support the needs of the process.</p>
<h2 id="emerging-vs-leading-strategy">Emerging vs leading strategy</h2>
<p>Now, you might argue, new technology can allow for new processes to develop. In that case, it would be technology that steers processes.</p>
<p>Take, for example, you buy a new ERP that greatly simplifies the way you handle your logistics. In this case, you have bought a new tool that has a particular way of working, and your previous way of working needs to be adapted to the new tool.</p>
<p>And that is correct, but it&rsquo;s also not as it should be.</p>
<p>We have currently only looked at processes and technology, there is also the strategy layer that has been left out till now. In the example before, we have what is called an emerging strategy. A strategy that organically grows from the way the organization functions. Some people also call it a passive or reactive strategy. <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>This way of working allows external parties to greatly influence the way we work. We are always chasing the ball, and most of our time will be spent trying to patch and connect everything, as the organization becomes a patchwork of disconnected ideas and incompatible ways of working..</p>
<p>The opposite of this is a leading strategy, where we dictate how the processes should run and match the software to that process. Here we are in control of what we want to achieve and how we are going to achieve it.</p>
<p>In terms of the previous example, that would be going to the market with the way (RFP) you want your logistics to be simplified. Find the closest match and try and adapt it to your way of working. Here you learn from the experiences that made your organization an enterprise in the first place. You don&rsquo;t get to this point by not knowing your market, product, customers and ways of working. Why risk all of that by just blindly implementing the way of working of a third party?</p>
<h2 id="so-waterfall">So waterfall?</h2>
<p>That does indeed give a very top-heavy and waterfall vibe to it<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, but that is not necessarily the case. Leading strategy is not static strategy: It&rsquo;s not because you plan out your strategy that you can&rsquo;t adapt it to new opportunities. It&rsquo;s just very important to take a look at where your strategy comes from, as that dictates the direction of the entire thing.</p>
<p>There are companies that use execution methods like waterfall or agile as a strategy, this is an extreme version of reactive strategy. A better method here is to take your learnings from the execution method and adopt it in your leading strategy. So strategy then delivery followed by a feedback loop.</p>
<p>Say, for example, you move your infrastructure to the cloud and part of your negotiated tier pricing includes a &ldquo;free&rdquo; database component. You would not blindly adopt said technology because it is available. Even if it is a better match for the application.You would have to take a look at why you would want to implement it. Ignoring whether or not this would lower the TCO (total cost of ownership) of the application, you will have to look at what the benefit is of the migration. If, for example, you were able to speed up the application by 30% or handle way more users at the same time, this all would be meaningless if your processes don&rsquo;t require it.</p>
<p>If your technology outpaces your process needs, that’s not innovation that&rsquo;s premature optimization. The role of technology is to support the people doing the work, not to chase hypothetical performance gains.</p>
<p>The technology is there to support the people in doing their jobs. If you just implement technology in the hopes that it will have a positive impact on the organization you are just trying to find a problem to your solution.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://frederickvanbrabant.com/blog/2025-05-16-people-processes-technology-and-information/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2025-05-16-people-processes-technology-and-information/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>This is not to be confused with the agile and &ldquo;move fast and break things&rdquo; way of working of startups. That would be an adaptive strategy.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>I&rsquo;m personally not the biggest opponent of waterfall <a href="https://frederickvanbrabant.com/blog/2024-07-19-architecture-in-an-agile-world/"




 target="_blank"
 


>https://frederickvanbrabant.com/blog/2024-07-19-architecture-in-an-agile-world/</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item></channel></rss>