<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Field Notes · Bartosz Frąckowiak</title>
    <link>https://bfrackowiak.pl/blog/</link>
    <atom:link href="https://bfrackowiak.pl/feed.xml" rel="self" type="application/rss+xml"/>
    <description>Weekly essays on software architecture, the people around it, and the corporate machine they form together.</description>
    <language>en</language>
    <lastBuildDate>Mon, 27 Jul 2026 23:00:01 +0200</lastBuildDate>
    <item>
      <title>A Feature Is Not a Service</title>
      <link>https://bfrackowiak.pl/blog/a-feature-is-not-a-service/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/a-feature-is-not-a-service/</guid>
      <pubDate>Mon, 27 Jul 2026 13:13:00 +0200</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>The ticket said &quot;saved filters&quot;. The real decisions were which service, whose identity, and how little to invent.</description>
      <content:encoded><![CDATA[<p>The ticket was small enough to fit in one sentence. Let users save a set of filters, give it a name, and load it again later. A default view on login. Maybe share one with a colleague.</p>
<p>The first design instinct in the room was a new service. It had a name before it had a schema: its own repository, its own database, its own deployment. That is the reflex a lot of us share, and I understand it. A new capability feels like it deserves a new home.</p>
<p>I have built that unnecessary home before, so I know its smell. One project&#039;s field notes here, a couple of scars, not a rulebook.</p>
<p>What shipped was one table in a service that already existed, and three decisions that had nothing to do with filters.</p>
<h2>The service that shouldn&#039;t exist</h2>
<p>Saved filters is, underneath the product language, a small piece of per-user CRUD. Create, list, edit, delete, one of them marked as default. That is not a system. That is a table and a handful of endpoints.</p>
<p>I did not fight the new service in the meeting. I asked it the three questions I ask every candidate service, out loud, one at a time. What data would it own that nobody else already owns? What lifecycle does it have that its host would not give it? What can fail inside it that should not take the feature down anyway? The answers came back borrowed, none, and nothing. The proposal did not need an opponent after that. It needed a smaller ticket.</p>
<p>So it went inside the service that already aggregates the data the filters run against. Same database, same identity plumbing, same logging and health checks and deploy pipeline. A new service would have inherited none of that for free and would have owed me all of it as work. <strong>A feature earns a new service when it has its own data, its own lifecycle, and its own reason to fail alone.</strong> Saved filters has none of those. It borrows all three.</p>
<p>This is the quiet side of the job, and <a href="/blog/decisions-have-an-architecture-too/">these are exactly the decisions that never reach a slide</a>. The service you chose not to create leaves no artifact behind. Nobody congratulates you for the microservice that isn&#039;t there.</p>
<figure class="post-figure"><img src="/assets/img/posts/a-feature-is-not-a-service/feature-vs-service.png" alt="What a new service would have to own, next to what a module simply reuses." width="2640" height="1520" loading="lazy" style="max-width:100%;height:auto"><figcaption style="font-size:.85em;color:var(--muted);margin-top:.4em">What a new service would have to own, next to what a module simply reuses.</figcaption></figure>
<h2>Whose filter is this</h2>
<p>The sharpest decision was the smallest field: the user id.</p>
<p>The naive version takes it from the request. The client says &quot;save this filter for user 4213,&quot; and the server obliges. It works in the demo. It is also a door: anyone who can call the endpoint can write into anyone else&#039;s collection by changing a number.</p>
<p>So <strong>the user id is never in the request body.</strong> It comes from the authenticated context the gateway already forwards downstream, the same context every other call in the platform is scoped by. The client cannot name a user. It can only be one. <strong>The safest user id is the one the client never gets to type.</strong> Isolation stops being a check I have to remember to write, and becomes a property of where the value comes from.</p>
<figure class="post-figure"><img src="/assets/img/posts/a-feature-is-not-a-service/identity.png" alt="The user id comes from the authenticated context, never from the request body." width="2800" height="1440" loading="lazy" style="max-width:100%;height:auto"><figcaption style="font-size:.85em;color:var(--muted);margin-top:.4em">The user id comes from the authenticated context, never from the request body.</figcaption></figure>
<h2>Copy the nearest working thing</h2>
<p>There is always a temptation to design the storage from scratch. Saved filters can hold nested AND/OR logic, so surely it needs a clever relational schema.</p>
<p>It did not. The platform already had a filter model, the exact tree the search feature passes around every day. A saved filter is just that tree, parked in a JSON column, next to the user who owns it. I found the closest existing feature that already did per-user CRUD with that model, and I copied its shape almost line for line. <strong>Reuse over invention is not laziness.</strong> It is the difference between a feature that behaves like the rest of the system and one that surprises whoever reads it next. It is also <a href="/blog/the-model-is-the-easy-part/">the rule that kept an MCP connector boring</a> last week: reuse the trust you already have, invent nothing you will have to guard.</p>
<p>This is the <a href="/blog/mathematicians-vs-lawyers/">mathematician&#039;s move</a>: do not add a new concept when an attribute of an existing one will do. The people who wanted a rich new &quot;saved filter&quot; model were about to breed a small universe of edge cases that a JSON blob of an existing tree simply never has.</p>
<figure class="post-figure"><img src="/assets/img/posts/a-feature-is-not-a-service/module.png" alt="Saved filters as one module inside an existing service: same database, identity, and pipeline." width="2960" height="1520" loading="lazy" style="max-width:100%;height:auto"><figcaption style="font-size:.85em;color:var(--muted);margin-top:.4em">Saved filters as one module inside an existing service: same database, identity, and pipeline.</figcaption></figure>
<h2>Draw the line at durability</h2>
<p>One more decision, easy to get wrong in the other direction: how much to persist.</p>
<p>Part of the ticket was really about session state. Keep my filters while I click from the list into a detail and back. That is not a database problem. That is the frontend holding its own state for the length of a visit. I have cleaned up after the other choice before: a store of &quot;recent selections&quot; that outlived its feature by years, rows nobody dared delete because nobody could say who still read them. The backend stores the thing you named and want back next week. It does not store the thing you will forget by lunch. Persisting transient state is how you end up with a table full of ghosts nobody asked for.</p>
<h2>Try this</h2>
<p>Look at the last feature you gave its own service. Strip the product language off it and ask three questions. Does it own data nobody else owns? Does it have a lifecycle of its own? Can it fail by itself without taking a real capability down with it?</p>
<p>If it cannot answer yes three times, it was never a service. It was a table wearing a lanyard. What would you fold back in?</p>
]]></content:encoded>
      <category>Software Architecture</category>
      <category>Domain-Driven Design</category>
      <category>Decision Making</category>
      <category>Systems Thinking</category>
      <category>API Security</category>
    </item>
    <item>
      <title>The Model Is the Easy Part</title>
      <link>https://bfrackowiak.pl/blog/the-model-is-the-easy-part/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/the-model-is-the-easy-part/</guid>
      <pubDate>Mon, 20 Jul 2026 09:00:00 +0200</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>I put an MCP server in front of a production API. The hard parts were tokens and state, not the model.</description>
      <content:encoded><![CDATA[<p>A request landed on my desk that sounded like the future: let customers point their AI tools at our data. Claude, Copilot, whatever agent they run. Ask a question in plain language, get an answer back from the platform.</p>
<p>Everyone in the room pictured the model. I did too, for about a minute. Then the only question that actually mattered showed up, and it had nothing to do with AI: how does the robot log in?</p>
<p>I want to be honest about the pull I felt. New shiny problem, new shiny solution. Part of me wanted to design something novel for it, a bespoke way for agents to authenticate, because novel is fun and novel gets noticed. These are field notes from one connector, scars included, not a security standard.</p>
<p>The thing I actually shipped is almost aggressively boring. And that was the point.</p>
<h2>A connector that adds nothing new</h2>
<p>The connector is an <em>MCP server</em>, an implementation of the Model Context Protocol, sitting in front of an existing REST API. It translates &quot;answer questions about my data&quot; into calls the API already knows how to serve. No new data path. No new store. It reads, it does not write.</p>
<p>If that shape sounds familiar, it should. It is an anti-corruption layer, the <a href="/blog/the-anti-corruption-layer-is-for-people-too/">same pattern I keep prescribing to human departments</a>, pointed at AI clients instead. The core API keeps its model. The connector absorbs the foreign one. The AI gets a clean, narrow surface and never touches the inside.</p>
<p>Here is the rule I set on day one: <strong>the connector introduces no new way to trust anyone.</strong> The customer already holds credentials for the API. They already exchange those credentials for a token at an endpoint that already exists. The connector does exactly that, on their behalf, and nothing else. No new grant type, no new token broker, no new key store to guard.</p>
<figure class="post-figure"><img src="/assets/img/posts/the-model-is-the-easy-part/container.png" alt="The connector as a thin, read-only proxy that reuses the existing edge, identity server, and API." width="2960" height="1520" loading="lazy" style="max-width:100%;height:auto"><figcaption style="font-size:.85em;color:var(--muted);margin-top:.4em">The connector as a thin, read-only proxy that reuses the existing edge, identity server, and API.</figcaption></figure>
<h2>Who holds the token now</h2>
<p>There is one thing that quietly changes, and you have to own it.</p>
<p>Today the customer&#039;s own client holds the token. With a hosted connector in the middle, my process holds it. It reads the credentials off the request, exchanges them for a short-lived token, caches that token, calls the API, and refreshes when the API says the token expired.</p>
<p>So I treated the cache like it was radioactive. The key is the client id plus a hash of the secret, never the secret itself. The token lives for minutes. And the secret never enters the model&#039;s context, not once, because the one thing worse than leaking a credential is leaking it into a transcript that gets replayed to a language model ;) The telemetry layer redacts it too.</p>
<p>None of that is AI work. It is the plumbing you would build for any proxy that borrows someone&#039;s identity. The AI just made the stakes legible.</p>
<figure class="post-figure"><img src="/assets/img/posts/the-model-is-the-easy-part/token-lifecycle.png" alt="The token lifecycle. The proxy borrows the identity server-side and caches only a short-lived token." width="2960" height="1520" loading="lazy" style="max-width:100%;height:auto"><figcaption style="font-size:.85em;color:var(--muted);margin-top:.4em">The token lifecycle. The proxy borrows the identity server-side and caches only a short-lived token.</figcaption></figure>
<h2>The boring option won on purpose</h2>
<p>There were three real ways to do the auth. Machine login, reusing the existing credential exchange. An issued key with a token broker in front. A full browser login with per-user consent. Each is defensible. Each also has an architecture, and <a href="/blog/decisions-have-an-architecture-too/">decisions carry an architecture whether you draw it or not</a>.</p>
<p>Not everyone in the room wanted boring. Novel had fans, and I did not argue with them. I asked <a href="/blog/questions-are-an-architects-sharpest-tool/">the same three questions</a> of each option instead: who rotates this credential, who answers when it leaks, which team owns the new moving part? For the machine login, every answer was &quot;the people who already do&quot;. For the other two, every answer was a new promise someone would have to keep. The room did not pick boring because I pushed it. Boring is what survived the questions.</p>
<p>I chose the machine login, the one that invents the least. When security reviewed it, the finding was the sentence I had been aiming at the whole time: <strong>no new risk compared to using the API directly.</strong> That is not a compliment to my cleverness. It is the absence of my cleverness, on purpose. <strong>The interesting part of an AI feature is usually the least AI part of it.</strong></p>
<h2>The hard part was never the AI</h2>
<p>Where did the real time go? State.</p>
<p>The model is a stateless call and a stream of tokens back. Fine. But those streams are long-lived connections, and long-lived connections do not love being spread across a fleet of identical instances behind a load balancer. The in-process token cache that is so tidy on one node becomes a coordination problem on twenty. Sticky sessions, a shared cache, backpressure on open streams: that is the actual engineering, and <strong>none of it has &quot;AI&quot; in the name.</strong></p>
<p>My resolution followed the same rule as the auth: invent the least. Streams pin to the node that opened them, plain session affinity at the gateway we already run. Tokens stay in each node&#039;s own memory, and a cache miss costs one extra exchange against an endpoint that was built for that traffic. I looked hard at a shared token store and said no. New infrastructure, secrets at rest in one more place, one more thing to guard and to explain in a review. Twenty small caches that are each allowed to be wrong beat one big cache that has to be right.</p>
<p>Add the unglamorous edge, reused wholesale instead of rebuilt: the same gateway, the same web firewall, the same per-tenant rate limiting, the same audit trail the rest of the platform already trusts. The connector earns its keep by being <strong>indistinguishable from a normal service</strong>, right up until you ask it a question in English.</p>
<figure class="post-figure"><img src="/assets/img/posts/the-model-is-the-easy-part/scale.png" alt="The stateless request path scales sideways. Long-lived streams and shared token state are the real work." width="2920" height="1440" loading="lazy" style="max-width:100%;height:auto"><figcaption style="font-size:.85em;color:var(--muted);margin-top:.4em">The stateless request path scales sideways. Long-lived streams and shared token state are the real work.</figcaption></figure>
<h2>Try this</h2>
<p>Next time an AI capability lands on your roadmap, write down the auth and the state design before you write a single prompt. If the interesting decisions are all about identity, custody, and connection lifecycle, and the model is a plain call at the end, you are probably building it right.</p>
<p>So what did you have to add to let the AI in? And if the answer is &quot;a new way to trust something,&quot; are you sure you needed it?</p>
]]></content:encoded>
      <category>Software Architecture</category>
      <category>Artificial Intelligence</category>
      <category>API Security</category>
      <category>Domain-Driven Design</category>
      <category>Decision Making</category>
    </item>
    <item>
      <title>Mathematicians vs. Lawyers</title>
      <link>https://bfrackowiak.pl/blog/mathematicians-vs-lawyers/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/mathematicians-vs-lawyers/</guid>
      <pubDate>Mon, 13 Jul 2026 09:00:00 +0200</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>Two thinking archetypes I keep meeting in architecture rooms, and why your system needs both.</description>
      <content:encoded><![CDATA[<p>Put two smart people in front of the same business problem and watch what happens.</p>
<p>One of them walks to the whiteboard and starts drawing boxes and arrows. Entities, relations, flows. Within five minutes the whole domain is a living organism on the wall, and half the room quietly wonders where the actual requirements went.</p>
<p>The other one opens a document and starts listing cases. What if the customer is from Germany? What if the invoice was already corrected? What if the correction arrives after the fiscal year closes? Twenty minutes later you have a table with forty rows, and the whiteboard person quietly wonders why we are debugging the universe.</p>
<p>After years of working as a Solution Architect, I started recognizing these two recurring archetypes of thinking. I call them <strong>Mathematicians</strong> and <strong>Lawyers</strong>.</p>
<p>Before anyone gets offended: there are many more archetypes than two, and nobody is purely one of them. These are modes of thinking, not personality boxes. But once you see these two, you cannot unsee them.</p>
<h2>The Lawyers</h2>
<p>Lawyers live in a <strong>linear world of rules</strong>. Given condition A and condition B, then C applies, unless exception D. Their thinking moves like a legal act: precise, sequential, exhaustive.</p>
<p>And they are <em>good</em>. A Lawyer is the person who catches the case that would have exploded in production three months after go-live. While the rest of the room admires the elegant model, the Lawyer asks the boring, deadly question: &quot;and what happens on February 29th?&quot;</p>
<p>Their natural artifact is the Document. The big one.</p>
<h2>The Edge Case Explosion</h2>
<p>This is where corporate culture enters the story, and where my observation stopped being about individuals and started being about the system.</p>
<p>A product company has a very hard time saying <strong>no</strong>. Every client exception gets accepted. Every &quot;just this one special case&quot; becomes a requirement. And the Lawyers do exactly what they are brilliant at: they faithfully encode all of it. Rule by rule, exception by exception.</p>
<p>The document grows. Fifty pages. Eighty. Hundreds of edge cases, each one individually justified.</p>
<p>Let&#039;s be honest with ourselves: <strong>nobody reads that document carefully.</strong> Nobody except other Lawyers. The rest of us skim it, nod in the review meeting, and approve. The knowledge is technically written down and practically inaccessible.</p>
<p>There is also a trap hiding in plain sight. A thick specification fits perfectly into corporate optics: <strong>volume looks like effort</strong>. &quot;Big&quot; reads as &quot;a lot of work was done.&quot; A three-hundred-page document is impressive in a way that a single clean diagram will never be. So the organization quietly rewards the explosion instead of questioning it.</p>
<p>What you end up with is complexity that nobody ordered. It was delivered one accepted exception at a time, by people doing their jobs well, inside a culture that couldn&#039;t refuse anyone.</p>
<h2>The Mathematicians</h2>
<p>Mathematicians think in <strong>abstract systems</strong>. They see high-level entities and how they cooperate. Their world is not made of rules but of <strong>relations between objects</strong>: how those relations evolve over time, what constraints must hold, who governs what.</p>
<p>Show a Mathematician eighty edge cases and they will not see eighty problems. They will ask a different question: <em>what is wrong with the model that keeps generating these exceptions?</em> Change the model, and whole families of edge cases simply stop existing. Nobody has to handle a case that the design made impossible.</p>
<p>Their curse: <strong>the impact is less visible.</strong> One diagram against three hundred pages. When a Mathematician does the job well, the visible result is… nothing. Complexity that never appeared. Incidents that never fired. An integration that didn&#039;t need a workaround. Try putting <em>that</em> on a promotion slide.</p>
<p>In a culture where volume equals work, Mathematicians chronically look underemployed. Right up until they leave, and the edge cases start breeding unsupervised.</p>
<p>And to be fair, Mathematicians have their own failure mode. Left unchecked, they drift into astronaut architecture: models so abstract they no longer touch the ground. Guess who pulls them back? The Lawyer, with one concrete, annoying, absolutely real corrected invoice from Germany.</p>
<h2>Two Worlds, One System</h2>
<p>This is not a war, and I&#039;m not writing this to declare a winner.</p>
<p>The best design sessions I have ever seen had both minds in the room. The Mathematician reshapes the model so the exceptions dissolve; the Lawyer stress-tests the new model with cases brutally imported from reality. Lawyers keep the Mathematicians honest. Mathematicians keep the Lawyers&#039; world from exploding.</p>
<p>A room full of Lawyers produces an edge case explosion with a spreadsheet to track it. A room full of Mathematicians produces a beautiful abstraction that dies on first contact with a real customer. If everyone in your design review thinks the same way, <em>that</em> is your biggest architecture risk, and it&#039;s not in any diagram.</p>
<p>These two worlds complement each other. Use both. Deliberately.</p>
<h2>Where I Choose to Stand</h2>
<p>My private rule as a Solution Architect: <strong>stay at the mathematical level. Don&#039;t descend into the details.</strong></p>
<p>Not because details are beneath me. Because the moment I dive into edge cases, I become one more Lawyer in a room that usually has plenty of them already, and the system-level perspective goes vacant. Every edge case has an owner somewhere. The relations <em>between</em> systems often have none. That empty chair is exactly where an architect should sit.</p>
<p>I won&#039;t pretend it&#039;s easy. Diving into details gives you the quick dopamine of being concretely useful. Holding the abstract level means accepting that your best work will often be invisible: the complexity that never happened.</p>
<p>So, next time you are in a design review, look around the room. Who is writing the rules, and who is drawing the relations? Which one are you when the pressure rises?</p>
<p>And more importantly: who is holding the other half of the picture?</p>
]]></content:encoded>
      <category>Software Architecture</category>
      <category>Systems Thinking</category>
      <category>Corporate Culture</category>
      <category>Engineering Leadership</category>
      <category>Team Collaboration</category>
    </item>
    <item>
      <title>Pricing the Invisible</title>
      <link>https://bfrackowiak.pl/blog/pricing-the-invisible/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/pricing-the-invisible/</guid>
      <pubDate>Mon, 06 Jul 2026 09:00:00 +0200</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>Your best architecture work produces nothing observable. Backlogs only rank observables. Let&#039;s fix the mismatch.</description>
      <content:encoded><![CDATA[<p>The prioritization meeting is where architecture goes to lose. I&#039;ve watched this match for years, and the script barely changes. A feature walks in with a number attached (&quot;+2% conversion,&quot; &quot;unlocks segment X&quot;) and takes its seat at the top of the backlog. Then the architecture item walks in: &quot;we should consolidate the integration layer.&quot; No number. Just an architect, gesturing at the future.</p>
<p>Final score, season after season: features 500, architecture 3. And architecture only scored after production incidents did the lobbying.</p>
<p>Standard disclaimer: private observations, not science. But I suspect your scoreboard looks similar.</p>
<h2>The Curse of Invisible Work</h2>
<p>In <a href="/blog/mathematicians-vs-lawyers/">Mathematicians vs. Lawyers</a> I wrote about the thinking archetype whose impact is chronically less visible: change the model, and whole families of problems silently stop existing. This is that curse, operationalized in a backlog.</p>
<p>When architecture work succeeds, the visible result is <em>nothing</em>. The incident that never fired opens no ticket. The complexity that never arrived has no demo. The integration that didn&#039;t need a workaround generates no praise thread. Prevention, by definition, deletes its own evidence.</p>
<p>And backlogs, the machinery we use to decide what matters, only rank observables. A backlog is a list of things that will visibly happen. It has no column for things that will invisibly <em>not</em> happen. <strong>The counterfactual has no product owner.</strong></p>
<h2>Why &quot;The Architect Wants It&quot; Always Loses</h2>
<p>So how does architecture work usually get argued for? With authority. &quot;This is important.&quot; &quot;Trust me, this will bite us.&quot; &quot;It&#039;s best practice.&quot;</p>
<p>Now watch what happens in the room. The feature brings a number; the architecture item brings an adjective. In any prioritization forum, <strong>a weak number beats a strong adjective</strong>, even when everyone privately suspects the number is fiction. Arithmetic, even bad arithmetic, gives people something to compare, and comparison is the only operation a backlog knows.</p>
<p>&quot;Trust me&quot; is a real currency, but it doesn&#039;t scale. It spends your personal credibility, it expires when you leave the room, and it loses every time to a spreadsheet cell. If invisible work is going to survive prioritization, it needs a number of its own.</p>
<h2>Give It an Honest Number</h2>
<p>My own fix is borrowed from my tech-radar practice (described in <a href="/blog/from-signals-to-blips/">From Signals to Blips</a>): score architecture and debt items on shared, explicit anchors.</p>
<pre><code>importance = multiplicity × severity × strategic_weight
</code></pre>
<p><em>Multiplicity</em>: how many teams or systems the item touches. You count them, which makes this the hardest factor to argue with. <em>Severity</em> (1–3): from inconvenience to actively blocked work, each level anchored in words everyone agreed on beforehand. <em>Strategic weight</em> (1–3): from local cleanup to a decision that sets an organization-wide precedent.</p>
<p>The multiplication matters: an item has to score on several dimensions at once to rank high. But the real magic is elsewhere, in <strong>comparability</strong>. Two architecture items scored on the same public anchors can be ranked against each other, and against features, without a single euro of fictional ROI. The anchors are published, so any score can be challenged; &quot;why severity 3?&quot; becomes a productive argument instead of a status contest. And scoring takes minutes, not a discovery sprint.</p>
<p>The fight changes shape immediately. The architect stops begging and starts comparing. &quot;This scores 24 because it blocks four teams at severity 2 and sets a precedent&quot; is a sentence a product owner can <em>work with</em>: accept it, challenge it, trade against it. &quot;We really should do this&quot; never was.</p>
<h2>The Trap: Fake Precision</h2>
<p>Now the failure mode, because there is one, and it&#039;s seductive.</p>
<p>Having learned that numbers win, the tempted architect goes one step further: &quot;this refactoring will save €340,000 per year.&quot; A currency! Decimals! Nobody can verify it, everybody smells it, and you&#039;ve just converted your credibility into a one-time payment. I wrote about this exact mechanism in <a href="/blog/data-driven-theater/">Data-Driven Theater</a>. Manufactured precision is worse than honest vagueness, because it teaches the room to distrust <em>all</em> your numbers, including the honest ones.</p>
<p>The anchored score works precisely because it declares its own crudeness. It says: this is a triage instrument built on public assumptions (multiplicity counted, severity argued, weight assigned), not an audited business case. Every input can be defended line by line, which is exactly what makes it safe to use. <strong>A number you can&#039;t defend is a loan against your credibility.</strong></p>
<p>So don&#039;t fake ROI. Publish the anchors, score in the open, state the assumptions, and let people attack the inputs instead of your authority.</p>
<p>The invisible work will never fully market itself. Prevention deletes its own evidence, and no formula changes that. But there is a difference between invisible and unpriced. You can&#039;t make the counterfactual observable. You <em>can</em> make it comparable.</p>
<p>One exercise before your next prioritization meeting: take the most valuable thing your team never shipped a ticket for (the outage that didn&#039;t happen, the rewrite that turned out unnecessary) and score it. Multiplicity, severity, strategic weight.</p>
<p>Then the real question: what&#039;s the biggest number sitting invisible in your organization right now?</p>
]]></content:encoded>
      <category>Software Architecture</category>
      <category>Technical Debt</category>
      <category>Prioritization</category>
      <category>Engineering Leadership</category>
      <category>Data-Driven Decision Making</category>
    </item>
    <item>
      <title>Multi-Agent Is an Org Chart</title>
      <link>https://bfrackowiak.pl/blog/multi-agent-is-an-org-chart/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/multi-agent-is-an-org-chart/</guid>
      <pubDate>Mon, 29 Jun 2026 09:00:00 +0200</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>I designed a multi-agent AI system and accidentally rebuilt the org chart, failure modes included.</description>
      <content:encoded><![CDATA[<p>Recently, as part of our AI platform work, I&#039;ve been designing a multi-agent experience: a supervisor agent that understands the user&#039;s intent and routes work to specialist agents, each owning a slice of the domain, with escalation paths for the cases nobody can handle.</p>
<p>Somewhere around the third iteration of the target-architecture diagram I stopped and started laughing. Boxes with narrow responsibilities. A coordinator on top. Formal handoffs. Escalation paths. I had seen this diagram before: in every HR tool I&#039;ve ever been onboarded into.</p>
<p><strong>I wasn&#039;t designing a system. I was designing an organization.</strong> And the dozen-plus years I&#039;ve spent watching human organizations misbehave suddenly became the most relevant architecture experience I own.</p>
<p>Field notes from one solution design follow. Private observations, not a paper.</p>
<h2>Same Primitives, Different Substrate</h2>
<p>Strip the buzzwords and a multi-agent architecture is built from exactly the primitives an org designer uses: <strong>roles</strong> (specialist agents with narrow charters), <strong>handoffs</strong> (who passes work to whom, in what format), <strong>escalation paths</strong> (what happens when an agent is out of its depth), and <strong>trust boundaries</strong> (whose output is taken at face value and whose gets verified).</p>
<p>This is not a cute metaphor. The primitives are identical because the underlying problem is identical: <strong>coordinating limited intelligences with partial context toward a shared goal.</strong> For humans the limits are attention and working memory; for agents, the context window. Same constraint, different units.</p>
<p>The parallels get uncomfortably specific:</p>
<ul>
<li><strong>Context windows are span of control.</strong> A manager can effectively lead only so many people before nuance drops on the floor. An agent can hold only so much context before it starts confidently forgetting. In both cases the failure smells the same: dropped threads, generic answers, work quietly reinvented twice.</li>
<li><strong>Agent-to-agent contracts are… contracts.</strong> Schemas, expectations, what &quot;done&quot; means. Two teams with a fuzzy interface produce meetings; two agents with a fuzzy interface produce hallucinated handoffs. The meetings are cheaper.</li>
<li><strong>Decomposition is charter-writing.</strong> Deciding what each agent owns is the same act as writing team charters, and it fails the same way, through overlaps (two agents both &quot;own&quot; the answer) and gaps (a question no agent considers theirs).</li>
</ul>
<h2>Supervisors Are Middle Management</h2>
<p>The supervisor pattern (one orchestrator routing everything) is multi-agent middle management. And it fails exactly the way middle management fails:</p>
<p><strong>The bottleneck.</strong> Route every interaction through one coordinator and you&#039;ve serialized your parallelism. One overloaded node, everyone else idle. Any resemblance to a manager whose calendar is the company&#039;s critical path is fully intended.</p>
<p><strong>The telephone game.</strong> Each relay compresses. The specialist&#039;s rich answer becomes the supervisor&#039;s summary, which becomes a summary of a summary. Fidelity degrades per hop. In orgs we call it &quot;communication overhead&quot;, in agents &quot;context loss&quot;, and it&#039;s the same tax.</p>
<p><strong>Empire building.</strong> Give a supervisor agent loose instructions and it starts <em>doing</em> the work instead of routing it: answering domain questions itself, badly, because delegating &quot;feels&quot; riskier than acting. I have met this middle manager. Several times. Some of them were prompts.</p>
<p>The fixes are org-design fixes, translated: push decisions down to the specialists, allow direct channels between agents that collaborate often (not everything needs to go &quot;up&quot;), and define escalation <em>criteria</em> instead of escalation <em>habits</em>. If your supervisor agent starts scheduling coordination meetings between the other agents, turn the computer off ;)</p>
<h2>Guardrails Are Governance</h2>
<p>Every serious multi-agent design ends up with guardrails: what an agent may do autonomously, what requires human approval, what gets logged for audit. Rename these and you get a governance framework, complete with autonomy budgets, approval gates, and audit trails. The corporate déjà vu is total.</p>
<p>And the same tension applies. Over-govern and you build agent bureaucracy: every action awaiting sign-off, throughput dying politely, the system technically safe and practically useless. Under-govern and you&#039;re one confident hallucination away from an incident report. Organizations have been tuning this dial for a century, and the honest answer in both worlds is that the dial never stops needing tuning. You&#039;re not choosing a setting; you&#039;re accepting a job.</p>
<h2>The Mirror</h2>
<p>What this exercise actually gave me, beyond a chatbot architecture:</p>
<p>Organizational experience turned out to be directly transferable. Clear charters beat detailed procedures; the smallest unit that can own an outcome end-to-end should own it; and you should never create a coordinator role to fix a communication problem that a better contract would fix for free.</p>
<p>But the transfer runs both ways, and that&#039;s the part I can&#039;t stop thinking about. When you design coordination from scratch, with no politics, no history, no feelings to spare, you would never include a layer whose only function is summarizing upward. You would never require every hand-off to pass through one node. You would never let two roles both vaguely own the same outcome, and you would never leave an escalation path undefined.</p>
<p>Then you look at your real org chart and ask why it&#039;s full of things you would never design. The polite version of the answer is &quot;history&quot;. I explored the human side of sitting inside such a structure in <a href="/blog/technical-leader-identity-disorder/">Technical Leader — Identity Disorder</a>; designing agents is the first time I&#039;ve gotten to sit on the other side of the whiteboard.</p>
<p>There is a Conway&#039;s law for agents, too: left unattended, your multi-agent architecture will faithfully copy your organization&#039;s communication patterns, including the dysfunctional ones, because its designers can&#039;t easily imagine anything else. We nearly shipped our org chart into the prompt. I suspect we weren&#039;t the first.</p>
<p><strong>Multi-agent design is org design with a compiler.</strong> For the first time, our coordination theories produce stack traces instead of opinions.</p>
<p>So here&#039;s the question I&#039;d leave you with: if you rebuilt your organization the way you&#039;d architect a multi-agent system (from scratch, charters first, no history to respect), <strong>which of your current roles would survive?</strong></p>
<p>And if the answer makes you uncomfortable: the agents are ready whenever you are.</p>
]]></content:encoded>
      <category>Multi-Agent Systems</category>
      <category>Artificial Intelligence</category>
      <category>Organizational Design</category>
      <category>Software Architecture</category>
      <category>Conways Law</category>
    </item>
    <item>
      <title>Prompt-Driven Architecture</title>
      <link>https://bfrackowiak.pl/blog/prompt-driven-architecture/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/prompt-driven-architecture/</guid>
      <pubDate>Mon, 22 Jun 2026 09:00:00 +0200</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>Part three of the &quot;tools that scale&quot; series: what happens when the daily practice of a Solution Architect starts running on AI.</description>
      <content:encoded><![CDATA[<p>Open any active architecture workstream on my disk (a risk aggregation design, a multi-agent assistant, a gnarly integration decision) and you will find the same file at the root: <code>prompt.md</code>. It sits next to the diagrams and the decision records, and it outranks both. That file is not a note. It is the workstream&#039;s engine.</p>
<p>In <a href="/blog/from-signals-to-blips/">From Signals to Blips</a> I described how an AI sweep of squad reports feeds our tech radar, and in <a href="/blog/probabilistic-metrics/">Probabilistic Metrics</a> what that does to measurement. This one is more personal: how my everyday deliverables became prompt-driven, from solution designs through decision documents to diagrams. Field notes as usual, no science, a few scars.</p>
<h2>Every workstream carries its prompt</h2>
<p>Here is the practice, concretely. A solution design no longer starts in a document editor. It starts in <code>prompt.md</code>: the role, the task, the constraints, and pointers to context such as relevant decision records, discovery notes, and the previous design it supersedes. Diagrams have their own prompt file that describes what the drawing must answer and for whom. The weekly signal sweep is a prompt applied to fifteen squad reports. Even decision documents start from a prompt that knows which decision framework we use and who holds which role in it.</p>
<p>These prompts are <strong>versioned like code, diffed like code, and re-run like builds</strong>. When the output is wrong, I usually don&#039;t edit the output. I fix the prompt or the context and regenerate. It took me embarrassingly long to internalize the consequence: the conversation with the model is scaffolding, the generated document is a build artifact, and the thing worth reviewing, improving, and keeping is the prompt plus its context.</p>
<p>The chat is ephemeral. <strong>The prompt is the asset.</strong></p>
<h2>The context repository</h2>
<p>A prompt is only half of the machine. The other half is what it reads: a context repository (decision records, diagrams-as-code, discovery documents, meeting notes) organized <em>for agents to read</em>. Predictable folder structure. One concern per file. Explicit links instead of tribal knowledge. A short index that tells a fresh agent (or a fresh human, it turns out) where truth lives and in what order to load it.</p>
<p>Three years ago, in <a href="https://medium.com/@bfrackowiak/stop-calling-it-legacy-code-02308db28cb2">Stop Calling It Legacy Code</a>, I argued that a codebase is really a Knowledge Container: structured knowledge waiting for AI to read and act on it. This is that thesis, applied deliberately to the architecture practice itself. I now write documentation the way I used to write integration tests: knowing a machine will consume it, and that its quality directly determines the machine&#039;s output quality.</p>
<p>That changed one habit above all: when an agent produces nonsense, my first question is no longer &quot;what&#039;s wrong with the model?&quot; but &quot;what&#039;s stale in the container?&quot;</p>
<h2>What I delegate, and what I never will</h2>
<p>What delegates well, after a year of trying: first drafts of solution designs, sweeps across dozens of documents, diagram generation from a described intent, gap analysis of a design against a question checklist, consistency checks between artifacts that humans stopped cross-reading years ago. The pattern is clear. <strong>AI compresses everything that is reading, restructuring, or drafting.</strong></p>
<p>What never leaves the human: the decision. Our decision framework gives every architecture decision exactly one owner, and that owner is never a model. The agent can rank options, argue trade-offs, even draft the decision record, but the moment of commitment, the one someone must later defend in front of other humans, stays human. And so does the meeting where a design earns trust. People, it turns out, still buy architecture from people.</p>
<h2>Where it burned me</h2>
<p>An honesty section, because the glossy version of this article would be useless.</p>
<p>A generated context diagram once confidently included a component we had decommissioned a quarter earlier. The model didn&#039;t lie. My container did: the old decision record was still there, with nothing marking it superseded. Another time, a drafted design proposed a clean, plausible integration path between two systems that have never exchanged a byte. <em>Plausible</em> is the operative word and the real danger: <strong>wrong drafts no longer look wrong.</strong> Pre-AI, a bad document signaled itself with gaps and hand-waving. Now it arrives fully formed, well-written, and quietly fictional.</p>
<p>The countermeasures are unglamorous: every generated claim must cite the source document it came from; decision records get explicit superseded-by markers the day they die; and nothing leaves the workstream folder without a human pass. If that sounds like the discipline I keep demanding from data pipelines (documented preprocessing, cited sources, a human gate), it is. Same theater risk, same defense.</p>
<h2>The operator</h2>
<p>In the Legacy Code article I predicted engineers would become Operators: people who express intent, leverage knowledge containers, and judge results instead of typing every line. I just didn&#039;t expect the prediction to catch <em>me</em> first.</p>
<p>I author less than I used to. I operate a small system of prompts and context that produces first versions of nearly everything, and I spend the recovered hours where the leverage actually is: decisions, conversations, and asking better questions of both humans and machines.</p>
<p>So here is my closing challenge. Imagine writing your practice&#039;s <code>prompt.md</code> tomorrow, the file that would let a machine draft your typical week&#039;s work. Could you? Whatever you cannot yet put into that file is the part of your own job you haven&#039;t fully understood. That gap is worth more attention than any tool.</p>
]]></content:encoded>
      <category>AI Transformation</category>
      <category>Software Architecture</category>
      <category>Prompt Engineering</category>
      <category>Knowledge Management</category>
      <category>Engineering Leadership</category>
    </item>
    <item>
      <title>Probabilistic Metrics</title>
      <link>https://bfrackowiak.pl/blog/probabilistic-metrics/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/probabilistic-metrics/</guid>
      <pubDate>Mon, 01 Jun 2026 09:00:00 +0200</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>Why the most useful numbers in our AI transformation were never in a database.</description>
      <content:encoded><![CDATA[<p>In my previous article (<a href="/blog/from-signals-to-blips/">From Signals to Blips</a>) I described the engine room of our AI transformation: a tech radar fed by weekly AI sweeps over fifteen squad reports. Every week an agent reads the reports, extracts signals, scores them, and hands me a ranked candidate list for human validation.</p>
<p>This article is about the part of that machinery I can&#039;t stop thinking about. The scores that steer our architecture attention are not computed from clean, structured data. They are <strong>extracted by AI from messy human text: probabilistically, with a known and accepted margin of error.</strong></p>
<p>Usual disclaimer applies: what follows are field notes and private conclusions, not science. Although this time I have at least one production system behind them ;)</p>
<h2>The Clean Data Tax</h2>
<p>Classical metrics demand clean inputs: instrumented pipelines, structured events, agreed definitions. That cleanliness is a tax, and you pay it three times. Once in engineering, because somebody has to build and maintain the pipeline. Once in politics, because somebody has to win the meeting where &quot;active user&quot; gets defined. And once in coverage, because you only ever measure what you predicted, a year in advance, would be worth measuring.</p>
<p>And after paying all of that, the numbers still get quietly shaped in preprocessing before anyone sees a chart. I wrote a whole article about that theater (<a href="/blog/data-driven-theater/">Data-Driven Theater</a>).</p>
<p>The result is an organization precisely informed about a thin slice of reality and blind everywhere else. And the blind spots are not random. They are exactly the messy, human parts of the system: friction, workarounds, practices quietly spreading between teams, morale.</p>
<h2>What AI Actually Changes</h2>
<p>AI changes the <strong>input space of measurement</strong>. A metric no longer has to start its life as a structured event. It can be extracted from free-form text: a blocker described in a frustrated sentence, a win buried in the middle of a paragraph, the same practice named three different ways by three different teams.</p>
<p>On the radar this is very concrete. An agent decomposes every weekly report into atomic claims, classifies each one as a stopper or a promotable, and scores it on multiplicity, severity, and strategic weight. Nobody built a data pipeline for this. The &quot;sensor&quot; is a versioned prompt pointed at documents that already existed.</p>
<p>Any single extraction can be wrong. And that is not a bug waiting for a fix. That is the deal. You trade per-observation certainty for coverage of the whole organization.</p>
<h2>The Assumptions</h2>
<p>This trade is only sane under explicit assumptions. Here are mine, all learned (some painfully) from running the radar:</p>
<ol>
<li><strong>Accept the error rate, and say it out loud.</strong> A dose of acceptance is part of the contract. If a stakeholder expects audit-grade numbers, stop them at the door. This instrument doesn&#039;t produce those, and pretending otherwise is how trust dies.</li>
<li><strong>Never trust a single extraction. Trust convergence.</strong> One squad&#039;s report can be misread; five squads independently describing the same friction is a fact about the organization, whatever the error rate per reading. Redundancy replaces precision.</li>
<li><strong>Every number must cite its source.</strong> Our rule is <em>cite or it didn&#039;t happen</em>: each scored signal points back to the exact report, week, and section it came from. Probabilism without traceability is just noise with confidence.</li>
<li><strong>A human gate before consequences.</strong> Probabilistic metrics route attention; they must never issue verdicts. In our case nothing enters the radar without a human ticking a checkbox. AI proposes, humans dispose.</li>
<li><strong>The prompt is the preprocessing, so treat it like code.</strong> In <em>Data-Driven Theater</em> I argued that data manipulation lives in the preprocessing phase. Here, the preprocessing <em>is</em> the extraction prompt. So it&#039;s versioned, its scoring anchors have a changelog, and anyone can read it. If you hide the prompt, you&#039;ve rebuilt the theater with better lighting.</li>
<li><strong>Shared language still matters, but now it&#039;s the model&#039;s dictionary.</strong> An LLM will happily map three teams&#039; different vocabularies onto one concept. That&#039;s the superpower and the danger in one move: it can silently merge things that are not the same. The semantic contracts between people don&#039;t disappear; auditing how the model translates becomes part of the method.</li>
</ol>
<h2>What I Take Out of It</h2>
<p><strong>Coverage beats precision when the job is steering.</strong> I&#039;d rather be roughly right about the whole organization than precisely right about the five percent of it I managed to instrument. For triage (<em>where should attention go this week</em>) that is simply the correct trade.</p>
<p><strong>You can measure things that never had a pipeline.</strong> Converging practices, recurring friction, the mood of a transformation. On the radar, these signals routinely show up weeks before anything reaches a dashboard, because the dashboard for them was never going to be built.</p>
<p><strong>Metrics become disposable.</strong> When a new metric costs one prompt instead of one quarter, you can try ten and throw away eight. The metric portfolio stops being an investment you defend in meetings and becomes a hypothesis you test on Tuesday.</p>
<p><strong>The failure modes move; they don&#039;t vanish.</strong> Yesterday people gamed the data cleaning. Tomorrow they&#039;ll game the extraction. Same theater, new stage, and the same defense: documented preprocessing, cited sources, and a culture where asking &quot;how was this computed?&quot; is a compliment, not an attack.</p>
<p><strong>It won&#039;t survive an audit, and that&#039;s fine.</strong> A compass is not an accounting system. Use probabilistic metrics to decide where to look; once you know what you&#039;re looking at, instrument it properly. The two don&#039;t compete. One finds the question, the other defends the answer.</p>
<h2>One Question</h2>
<p>So here is what I&#039;d leave you with: <strong>what would you measure in your organization if a new metric cost one prompt instead of one quarter?</strong></p>
<p>Because that is the actual offer on the table now. The classic excuse for not measuring the human side of your system (too messy, too expensive, no pipeline) has just expired.</p>
]]></content:encoded>
      <category>Metrics</category>
      <category>Artificial Intelligence</category>
      <category>Data-Driven Decision Making</category>
      <category>AI Transformation</category>
      <category>Engineering Leadership</category>
    </item>
    <item>
      <title>From Signals to Blips</title>
      <link>https://bfrackowiak.pl/blog/from-signals-to-blips/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/from-signals-to-blips/</guid>
      <pubDate>Mon, 18 May 2026 09:00:00 +0200</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>How a tech radar became my favorite tool for driving an AI transformation across several hundred developers.</description>
      <content:encoded><![CDATA[<p>For the past months, as a Solution Architect, I&#039;ve been an active participant in our AI transformation. Several hundred developers. Fifteen squads across four tribes. One goal: change how software gets built.</p>
<p>And one painfully practical problem: <strong>how do you drive change at that scale?</strong></p>
<p>You can&#039;t attend every standup. You can&#039;t review every experiment. You can&#039;t personally convince three hundred people that a new way of working is worth their time. Whatever instrument you pick, it has to scale better than you do, and it has to survive contact with a real organization, where attention is scarce and opinions are loud.</p>
<p>My answer turned out to be a classic: <strong>a tech radar.</strong> Not the industry-wide kind you read once a year and forget, but an internal, living one: refreshed in editions, fed by real squad data. It scales beautifully, and it shows the state of the transformation in several dimensions at once. The <em>ring</em> tells you how much we trust something (Adopt, Trial, Assess, Hold), the <em>quadrant</em> tells you what kind of thing it is, the <em>movement</em> tells you where it&#039;s heading, and the <em>evidence</em> tells you who actually proved it. A radar is opinionated data. It doesn&#039;t just list what exists; it shows what we, as an organization, decided to do about it.</p>
<p><em>This is the first post in a short series about driving an AI transformation with tools that scale. We start with the engine room: how raw signals become blips.</em></p>
<h2>From Reports to a Score</h2>
<p>Every week, each squad files a structured report: adoption numbers, workflows integrated, wins, learnings, blockers, a maturity self-assessment. Fifteen files. Roughly two dozen distinct concerns surface every single week.</p>
<p>The old me would try to read everything and keep the picture in his head. The current me sends an AI agent through all of it with a very strict contract.</p>
<p>First, extraction. Every report is decomposed into <strong>atomic claims</strong>, each tagged with squad, week, and section. Rule number one: <strong>cite or it didn&#039;t happen.</strong> A signal that cannot point back to a concrete cell in a concrete report does not exist.</p>
<p>Second, classification. Only two classes of signal survive the sweep:</p>
<ul>
<li><strong>STOPPERS</strong>: anything that is stopping, or about to stop, a group of developers from making progress.</li>
<li><strong>PROMOTABLES</strong>: a working practice, skill, or integration that delivered impact and deserves to spread.</li>
</ul>
<p>Everything else is noise <em>for this run</em>, and it gets dropped. No parking of &quot;interesting but not actionable&quot; items. That discipline hurts at first and pays off every week after.</p>
<p>Third, the score:</p>
<pre><code>importance = multiplicity × severity × strategic_weight
</code></pre>
<p><em>Multiplicity</em> (1–5): how many squads independently raised the same signal. <em>Severity</em> (1–3): from local inconvenience to a blocked initiative (or, for promotables, from a quality-of-life win to a genuine capability unlock). <em>Strategic weight</em> (1–3): how far the decision ripples, from one squad to an architecture-level precedent.</p>
<p>The multiplication is deliberate. If any factor is low, the product collapses. A concern raised by one developer shouldn&#039;t outrank a cross-squad blocker, no matter how eloquently it was written. <strong>A signal has to score on all three dimensions to reach the top.</strong> The result is a number between 1 and 45, and it comes with bands: above 30 goes to the architecture forum this week, the middle band waits for the next radar edition, the rest is monitored.</p>
<p>It works in practice. Six squads independently naming the same delivery pressure lands at 45, top of the queue. A governance gap raised by a single squad, but blocking an entire platform branch, scores low on multiplicity yet stays visible thanks to strategic weight. The loudest voice stops mattering; the math does the listening.</p>
<p>One thing the score is <em>not</em>: a verdict on truth. It is not effort-weighted, not ROI-weighted, and it deliberately trusts the reports without cross-checking them. It measures <strong>signal strength, not validation status</strong>. It tells me what to discuss first, not what is true.</p>
<h2>From Score to Blips</h2>
<p>One part I consider non-negotiable: <strong>the AI never touches the radar.</strong></p>
<p>The sweep produces a candidate list. Each candidate is matched against the existing radar and typed: a new blip, a ring move, a confirmation of something already there, or, my favorite, a contradiction, where the field evidence says an adopted practice is breaking in real life.</p>
<p>Validation is human. Candidates go to a stakeholder meeting with a checkbox next to every decision. Ticked means it merges into the next radar edition, and every change lands in a changelog so trends across editions stay honest. The radar grew from 42 blips to over 50 this way. Every single addition can be traced back to the squad report that started it.</p>
<p>AI proposes. Humans dispose.</p>
<p>There is a bigger idea hiding inside this machinery. The radar&#039;s score is not computed from clean, structured data. It is extracted by AI from messy human text, probabilistically, with an accepted margin of error. I believe that pattern reaches far beyond one tech radar, and it deserves an article of its own. It&#039;s the next one in this series: <strong><a href="/blog/probabilistic-metrics/">Probabilistic Metrics</a></strong>.</p>
<p>Until then, one question. You&#039;ve just seen my tool for driving change across several hundred developers. What&#039;s yours, and does it scale better than you do?</p>
]]></content:encoded>
      <category>Tech Radar</category>
      <category>AI Transformation</category>
      <category>Software Architecture</category>
      <category>Engineering Leadership</category>
      <category>Data-Driven Decision Making</category>
    </item>
    <item>
      <title>Grinding Dijkstra at Senior Level</title>
      <link>https://bfrackowiak.pl/blog/grinding-dijkstra-at-senior-level/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/grinding-dijkstra-at-senior-level/</guid>
      <pubDate>Mon, 04 May 2026 09:00:00 +0200</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>Twelve years into my career, there&#039;s a repo on my disk where I practice graph traversals like a student. Here&#039;s why that&#039;s not humiliating.</description>
      <content:encoded><![CDATA[<p>Somewhere on my disk, between a SaaS I operate and an architecture tool I&#039;m building, sits a small C# solution nobody was ever supposed to see. Inside: <code>Dijkstra.cs</code>. Not production code. Practice. Preparation for a big-tech interview loop, the kind with shared editors, forty-minute slots, and a rubric.</p>
<p>Yes. Twelve-plus years in, carrying the title <em>Solution Architect</em>, and I was grinding shortest-path algorithms in the evenings like a third-year student before finals.</p>
<p>I&#039;m writing this down because almost nobody at my seniority level admits to it, and the silence creates a myth: that past some invisible line, you&#039;re beyond being tested. You&#039;re not. And the test, it turns out, is one of the most instructive systems I&#039;ve ever put myself through, just not for the reasons the recruiters advertise.</p>
<h2>The Humbling</h2>
<p>The first thing the loop teaches you: <strong>your title means nothing here.</strong> &quot;Solution Architect, twelve years of experience&quot; compresses into one line of a CV that the interviewer skimmed four minutes before the call. The loop doesn&#039;t care who I think I am. It cares what I can demonstrate, under pressure, in forty minutes, in a shared editor with someone watching my cursor.</p>
<p>And there is something bracingly <em>honest</em> about that. Inside a company, seniority accumulates a protective layer: reputation, relationships, past wins that answer questions before they&#039;re asked. The loop strips all of it. What&#039;s left is just you and a graph with weighted edges.</p>
<p>Algorithms, I discovered, are a language I used to speak fluently and now translate in my head. Exams, early jobs, the first competitive years: fluent. A decade-plus of architecture later, I still <em>read</em> the language perfectly well. But producing it at speed is different. You don&#039;t relearn BFS; the concept never left. You relearn <em>fluency</em>. Rebuilding it the second time is slower, stranger, and occasionally insulting to the ego.</p>
<p>I once wrote about the identity split this role produces (<a href="/blog/technical-leader-identity-disorder/">Technical Leader — Identity Disorder</a>). Interview prep is that split, sharpened to a point: it forces you to separate &quot;I am senior&quot; from &quot;I can demonstrate X under pressure in forty minutes.&quot; The first is a story. The second is a fact. The gap between those two sentences is exactly where the ego goes to negotiate.</p>
<h2>The System Behind the Loop</h2>
<p>Now the part where I stop feeling and start doing my actual job, which is looking at the system.</p>
<p>Big-tech hiring is not designed to measure your worth. It is designed to <strong>manage their risk at scale</strong>. A loop that processes tens of thousands of candidates optimizes ruthlessly against false positives (the bad hire who gets in) and accepts false negatives, the great engineer who gets rejected, as a tolerable loss. There are always more candidates. Rational for them. Brutal for you. Once you see that, a rejection stops being a verdict about your competence and becomes what it actually is: an artifact of someone else&#039;s risk function.</p>
<p>There&#039;s theater in it too, and everyone involved knows it. Candidates perform &quot;thinking out loud&quot; because the rubric rewards it. Interviewers calibrate against what a &quot;strong hire&quot; is supposed to sound like. Algorithms won as the measurement proxy not because anyone believes they&#039;re the job, but because <em>experience</em>, the thing that actually matters, cannot be assessed in an hour. Experience only shows over months. So the industry measures what fits in the slot, and quietly agrees not to mention that it&#039;s a proxy. And even the best-calibrated funnel delivers you into the same building where <a href="https://en.wikipedia.org/wiki/Peter_principle">the Peter Principle</a> is patiently waiting upstairs anyway ;)</p>
<p>I don&#039;t say this with bitterness. I say it with the professional respect of one systems designer examining another&#039;s trade-offs. I&#039;d probably design it similarly, and it would be similarly brutal.</p>
<h2>What I Actually Gained</h2>
<p>The part nobody tells you: the grind pays dividends completely unrelated to the offer.</p>
<p><strong>The fundamentals sharpened my day job.</strong> Complexity intuition is an architecture skill wearing a t-shirt. Estimating the blast radius of a fan-out, sensing why a graph traversal in a hot path should scare me, knowing when &quot;we&#039;ll cache it&quot; is a real answer. Same muscle, different dress code. A few weeks of deliberate practice tuned instincts I&#039;d been running on autopilot for years.</p>
<p><strong>I became a different interviewer.</strong> I&#039;ve sat on the hiring side of the table for years. After feeling the pressure from the candidate&#039;s chair again (the blank second where a thing you know simply won&#039;t come out), I redesigned my own interviews. Less performance, more collaboration. Pressure reveals nerves; it does not reveal competence.</p>
<p><strong>Market calibration changed how I behave at work, in the calm way.</strong> Knowing what you&#039;re worth outside your company removes a quiet fear from every disagreement inside it. Architecture debates stop being existential. You argue on merits, concede gracefully, and push hard when it matters, because your security no longer depends on winning the room.</p>
<h2>The Mirror</h2>
<p>So here&#039;s my genuinely private opinion, the one this whole post was building toward: <strong>every senior person should run an interview loop once every few years, even if they fully intend to stay.</strong></p>
<p>Not for the offer. For the mirror.</p>
<p>Seniority inside one organization slowly replaces feedback with comfort. People stop challenging you; context does half your thinking; your reputation answers questions your skills should be answering. The loop is the one mirror that hasn&#039;t been adjusted to flatter you.</p>
<p>One question to leave you with: when was the last time your competence was tested by someone who had no reason to be polite to you?</p>
<p>If you have to think back more than a few years, that repo full of graph algorithms might be worth creating after all.</p>
]]></content:encoded>
      <category>Technical Interviews</category>
      <category>Career Development</category>
      <category>Engineering Leadership</category>
      <category>Personal Growth</category>
      <category>Software Engineering</category>
    </item>
    <item>
      <title>A Company of One, Staffed by Agents</title>
      <link>https://bfrackowiak.pl/blog/a-company-of-one-staffed-by-agents/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/a-company-of-one-staffed-by-agents/</guid>
      <pubDate>Mon, 20 Apr 2026 09:00:00 +0200</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>Four shipping products, one person, and a field report on what AI actually changes about building software.</description>
      <content:encoded><![CDATA[<p>On a random Tuesday evening, my backlog looks like this: a database migration waiting for review in a multi-tenant Laravel SaaS, a release candidate for a native Android app, a pricing change that touches two marketing sites, and an integration test run against Poland&#039;s mandatory e-invoicing API.</p>
<p>The engineering department responsible for all of this is me. After hours. I also have a day job as a Solution Architect.</p>
<p>A few years ago this portfolio would have required a small software house: a couple of backend developers, a mobile developer, a tester, and somebody to herd them. Today it&#039;s one architect and a fleet of AI agents. What follows is a field report, not a victory lap. As usual, the failure modes turned out to be the most interesting part of the experiment.</p>
<h2>The Portfolio That Shouldn&#039;t Fit in One Head</h2>
<p>The product is <strong>Desta</strong>, an invoicing platform for the Polish market: a multi-tenant Laravel web application, a native Android app in Kotlin and Jetpack Compose, and two marketing sites for two different audiences. Under the hood sit the integrations nobody builds for fun: KSeF, Poland&#039;s mandatory e-invoicing system, where the XML is the legal invoice and the PDF is legally demoted to a &quot;visualization&quot;; a SOAP-based government registry lookup for company data; and JPK tax file generation. Roughly 990 automated tests hold the whole thing together.</p>
<p>One thing I had to learn the hard way: the constraint was never typing speed. <strong>The real bottleneck of a company of one is the number of decisions a single head can hold per day.</strong> Which schema change is safe. What happens to a finalized invoice when the law changes. Which release goes first. Code was never the scarce resource. Judgment is.</p>
<p>So &quot;operating&quot; the company means almost no typing at all. It means writing specifications, reviewing diffs, and verifying behavior. In <a href="https://medium.com/@bfrackowiak/stop-calling-it-legacy-code-02308db28cb2">Stop Calling It Legacy Code</a> I argued we&#039;re moving from engineers who write logic to <em>operators</em> who express intent and judge results. Desta is me testing that thesis with my own evenings and my own money.</p>
<h2>The Operating Model</h2>
<p>The setup that makes this survivable has three parts, and none of them is a clever prompt.</p>
<p><strong>Context files as boot loaders.</strong> Every sub-project carries its own AI context file: a maintained, code-verified document that tells an agent what the system is, which invariants are non-negotiable, and where the truth lives. One big-picture file routes between them. An agent lands in the polyrepo and navigates it like a well-onboarded hire, minus the three months of onboarding. This is the Knowledge Container idea from the legacy-code article, except now it&#039;s not a metaphor; it&#039;s infrastructure.</p>
<p><strong>Specs as prompts, versioned like code.</strong> Features start as written specifications that double as agent instructions. When the output is wrong, I don&#039;t argue in a chat window. I fix the spec file and rerun. The spec is the program; the conversation is just its execution log.</p>
<p><strong>Tests as the trust boundary.</strong> The ~990 tests are not there because I&#039;m diligent. They are there because agents don&#039;t get faith; they get assertions. Every agent-generated change lands against a mechanical safety net that doesn&#039;t care how confident the model sounded.</p>
<h2>What Breaks First</h2>
<p>Now the honest part.</p>
<p><strong>Review capacity breaks before generation capacity.</strong> Generating code is effectively free now; my evening attention is not. The bottleneck moved exactly where our transformation data at the day job said it would. I wrote about that in <a href="/blog/from-signals-to-blips/">From Signals to Blips</a>. A company of one doesn&#039;t run out of code. It runs out of reviewer.</p>
<p><strong>Trust calibration is a skill nobody teaches.</strong> Which diffs deserve line-by-line reading and which deserve test-suite faith? I&#039;ve settled on a simple rule: anything touching migrations or money gets my eyes on every line. Everything else gets the net. I still get it wrong in both directions, over-reading trivia and under-reading a &quot;trivial&quot; change that wasn&#039;t. My most instructive incident so far: an agent that made a red test green by weakening the assertion. The tests guard the code, but somebody still has to guard the tests.</p>
<p><strong>Domain decisions don&#039;t compress.</strong> Tax rules, pricing boundaries, what a correction invoice may reference. These take exactly as long as they took in 2020, because the hard part is understanding consequences, not producing code. <strong>AI compresses everything except responsibility.</strong></p>
<h2>The Moat Is Not the Code</h2>
<p>My conclusion after this experiment is uncomfortable for people selling code and comforting for people who know things: one architect plus agents is now a viable software company, for a <em>narrow</em> domain.</p>
<p>The code was never the moat; it&#039;s cheaper than ever. The moat is knowing which invoice field the law wants left blank, which government API header actually carries the session, which pricing edge case will generate a support email at 7 a.m. Domain knowledge, encoded into context files and tests, is the entire company. The agents are just very fast hands.</p>
<p>So here&#039;s my question for you: the &quot;I don&#039;t have a team&quot; excuse for not building that product you keep describing at lunch? It just expired. What&#039;s the new excuse? ;)</p>
]]></content:encoded>
      <category>AI Agents</category>
      <category>Solopreneurship</category>
      <category>SaaS</category>
      <category>Software Development</category>
      <category>Artificial Intelligence</category>
    </item>
    <item>
      <title>The Diagram That Cannot Lie</title>
      <link>https://bfrackowiak.pl/blog/the-diagram-that-cannot-lie/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/the-diagram-that-cannot-lie/</guid>
      <pubDate>Mon, 13 Apr 2026 09:00:00 +0200</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>Why I&#039;m building an architecture modeling tool where the diagram is physically incapable of drifting from the code.</description>
      <content:encoded><![CDATA[<p>Somewhere on your company&#039;s shared drive sits a folder full of architecture diagrams. Some are beautiful. Some took days of somebody&#039;s life. One of them is called <code>target-architecture-FINAL-v3-updated.drawio</code>. And every single one of them is lying to you right now.</p>
<p>I can say this with confidence because I&#039;ve drawn hundreds of them. As a Solution Architect, diagrams are supposedly my trade. And almost every diagram I&#039;ve ever produced started lying within a quarter of being finished: quietly, without malice, the way milk goes off.</p>
<h2>Death by Export</h2>
<p>Here is the lifecycle of a typical architecture diagram. It is born in a meeting, where it is genuinely useful. It gets polished, exported to PNG, pasted into a wiki page, and dies. Not <em>wrong</em> yet, but dying, because the system it describes keeps moving and the picture does not.</p>
<p>draw.io, Lucidchart, Enterprise Architect. Different price tags, same disease: <strong>the model and the truth live in two different places.</strong> Two sources, one guaranteed divergence. The only question is how many sprints it takes.</p>
<p>Reverse-engineering tools attack from the other side: read the code, generate the picture. Honest, but mute. You can look at the model; you cannot <em>decide</em> anything into it. A one-way street never closes the feedback loop.</p>
<p>Run this audit in your own team: find the diagram labeled &quot;current state&quot; and ask when it last matched the current state. I&#039;ve written elsewhere that <a href="/blog/diagrams-are-conversations-not-documentation/">diagrams are conversations, not documentation</a>: their value is consumed in the meeting where they&#039;re drawn. This article is about the other half of that thought: what would it take for a diagram to <em>be</em> documentation without rotting?</p>
<h2>The Code Is the Model</h2>
<p>My answer is a side project called <strong>Verso</strong>, so consider this both a design essay and a confession of bias ;)</p>
<p>The core bet: there is no database. None. The architecture model (bounded contexts, containers, data flows, ownership, risks, decisions) is stored as plain C# records in Git. When you open a workspace, the model lives in memory. When you edit something on the canvas, say rename a container, redraw a dependency, or re-parent a module, the tool rewrites the actual <code>.cs</code> files using Roslyn. Not code generation. Code <em>editing</em>.</p>
<p>And one rule overrides everything else in the project: <strong>round-trip fidelity.</strong> Any edit made in the UI must produce a git diff that contains only the intended change. Rename one element: the diff shows one identifier changed. No reformatting, no reshuffling, no collateral noise. If that rule ever breaks, the tool is broken, full stop, because fidelity is the only thing that makes the loop trustworthy enough to bet on.</p>
<p>Layout (positions, colors, views) lives in a small committed sidecar file, separate from the model. Content and presentation, finally divorced: the separation we&#039;ve preached for documents for thirty years and somehow never applied to diagrams.</p>
<p>The name comes from bookbinding. The <em>verso</em> is the reverse side of a page. The canvas is the verso of the code: same content, different face. A diagram cannot drift from the code when the diagram <strong>is</strong> the code.</p>
<h2>Architecture Review Becomes a Pull Request</h2>
<p>Storing the model as source sounds like a technicality. It isn&#039;t. It quietly changes who architecture belongs to.</p>
<p><strong>Review.</strong> An architecture change arrives as a pull request: diffable, commentable, blameable, revertable. That&#039;s governance for free, running on muscles every developer already has. No new committee, no new tool to police.</p>
<p><strong>History.</strong> You get <code>git log</code> for your architecture. Who moved this capability out of that context, when, and in which commit. That is the archaeology I have wished for in every architecture discussion of my career.</p>
<p><strong>Survival.</strong> The model outlives tools, licenses, and reorgs, because it&#039;s just source files in a repository. Nobody is held hostage by an export format.</p>
<p><strong>Views instead of copies.</strong> One long-living model, projected into audience-specific views: the executive gets capabilities, the team gets containers and flows. Today we redraw the same system five times for five audiences and then maintain five lies instead of one truth.</p>
<p><strong>Agents.</strong> This one matters more every month: AI agents read and write the same single truth as humans. No export/import ceremony, no stale copy for the bot. I argued in <a href="https://medium.com/@bfrackowiak/stop-calling-it-legacy-code-02308db28cb2">Stop Calling It Legacy Code</a> that a codebase is a knowledge container; Verso just extends the container to hold the architecture itself.</p>
<p>There&#039;s a sentence I keep coming back to when people ask why I&#039;d spend evenings on this: <strong>a diagram you can&#039;t diff is a rumor with better fonts.</strong></p>
<h2>Where I Landed</h2>
<p>After enough years of watching beautiful models rot, I simply stopped believing in any model that a compiler doesn&#039;t check. That disbelief nagged me long enough to become a side project, and the side project slowly became the tool I actually wanted as an architect: one long-living model, projected into audience-specific views, versioned like the code it describes, because it <em>is</em> code.</p>
<p>I don&#039;t know yet whether Verso becomes a real product or stays my favorite workshop experiment. I do know the question it asks is the right one.</p>
<p>So let me pass it on: how old is your &quot;current state&quot; diagram, and would you bet a production incident on it?</p>
]]></content:encoded>
      <category>Software Architecture</category>
      <category>Developer Tools</category>
      <category>Docs as Code</category>
      <category>Architecture Diagrams</category>
      <category>Side Projects</category>
    </item>
    <item>
      <title>Pricing Is Architecture</title>
      <link>https://bfrackowiak.pl/blog/pricing-is-architecture/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/pricing-is-architecture/</guid>
      <pubDate>Mon, 30 Mar 2026 09:00:00 +0200</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>The pricing page is an architecture diagram wearing marketing clothes.</description>
      <content:encoded><![CDATA[<p>Some time ago I redesigned the subscription model for Desta, the invoicing SaaS I build and operate for the Polish accounting market. I expected a marketing exercise: a spreadsheet, some tier names, an afternoon of choosing what goes into which column.</p>
<p>What I actually got was a database migration, new middleware, a rewritten registration flow, and changes to two marketing sites, plus a change plan that touched more than thirty controllers and a test suite of roughly nine hundred and ninety tests.</p>
<p>That afternoon produced the thesis of this post: <strong>a pricing page is an architecture diagram wearing marketing clothes.</strong> Every box on it is a system boundary. Every bullet point is an enforcement obligation. And somebody has to build all of it.</p>
<h2>The Model on Paper</h2>
<p>Desta serves two audiences on one platform, and they could hardly be more different. On one side, end clients: freelancers and small companies who want to issue invoices, buy a cheap self-serve plan online, and never talk to a human. On the other, accounting firms: businesses that manage dozens of clients, negotiate, and buy through a sales conversation.</p>
<p>Two audiences, two marketing sites, one codebase. And the separation isn&#039;t a slide-deck fiction. It&#039;s enforced all the way down to the marketing layer. When one page started attracting the wrong audience, the fix was a permanent redirect: the URL itself now performs audience segregation. Even your SEO is part of the entitlement system, whether you designed it that way or not.</p>
<p>Then come the tiers. Here&#039;s the thing nobody writes on the pricing page: <strong>every tier boundary is a promise, and every promise needs an enforcement point in code.</strong> A monthly document cap means a counter, a quota guard, and a decision about what happens at the limit. A notification allowance means metering. A &quot;this feature only in the higher plan&quot; bullet means a feature flag with a lifecycle. The cheapest tier, meanwhile, isn&#039;t really a revenue line at all. It&#039;s a distribution strategy that the codebase has to subsidize with real complexity.</p>
<h2>Where Pricing Bends the Code</h2>
<p>Now the part that surprised even me, and I designed the thing.</p>
<p>Serving B2C subscriptions inside a B2B multi-tenant architecture forced what I call the master-tenant pattern: individual self-serve clients live as per-user subscriptions <em>inside</em> one shared master tenant, while accounting firms hold classic tenant-scoped subscriptions. Two commercial models, one isolation model. The tenancy design has to keep them from ever bleeding into each other. That is not a billing detail. That is core architecture, dictated entirely by the pricing page.</p>
<p>It goes further:</p>
<ul>
<li><strong>Quota guards</strong> sit in the request path, counting documents against the monthly cap.</li>
<li><strong>Read-only mode</strong> kicks in when a subscription expires: the system must degrade gracefully, not amputate.</li>
<li><strong>Downgrades are deliberately blocked</strong>, a commercial policy decision compiled directly into middleware.</li>
</ul>
<p>Commercial policy, compiled into middleware. Read that again, because it&#039;s the whole point: the sales strategy doesn&#039;t sit <em>next to</em> the system; it executes <em>inside</em> it, on every request.</p>
<p>And then there&#039;s the migration tax. Early on, plan identifiers leaked into the codebase the way water finds cracks. Today, renaming a single plan slug means touching thirty-plus controllers and views, and re-validating nearly a thousand tests. <strong>A pricing change is a schema change.</strong> The spreadsheet doesn&#039;t know that. The codebase never forgets it.</p>
<h2>What the Pricing Meeting Forgets</h2>
<p>Which brings me to the organizational half of this story, because Desta is just my one-person laboratory; the same physics applies at any scale.</p>
<p>In most companies, pricing is decided in a meeting where no architect is present. A spreadsheet is blessed, a launch date is picked, and the decision lands downstream as years of constraints in code: entitlement checks, grandfathered plans, exceptions with exceptions. <strong>Architects belong in the pricing meeting</strong>, not to set the prices but to price the promises.</p>
<p>Billing is also where edge cases breed with a fury I&#039;ve rarely seen elsewhere: trials, proration, the client who downgrades mid-cycle, the grandfathered plan that predates the current model. If you&#039;ve read my <a href="/blog/mathematicians-vs-lawyers/">Mathematicians vs. Lawyers</a>, you know the archetype whose home turf this is: billing is the one domain where I <em>want</em> the Lawyer at the table, enumerating every case with a paragraph number. The Mathematician&#039;s job is to design a model in which most of those cases cannot exist; the Lawyer&#039;s job is to catch the ones that can.</p>
<p>And one hard-won rule: <strong>entitlements deserve a bounded context.</strong> A dedicated place in the architecture that answers exactly one question: <em>what is this account allowed to do right now?</em> Smear that logic across controllers and views, and you get the worst failure mode software offers: you can&#039;t debug revenue. A support ticket that says &quot;client paid but can&#039;t issue invoices&quot; turns from a five-minute lookup into an archaeology expedition.</p>
<h2>Show Me Your Pricing Page</h2>
<p>Here&#039;s my private conclusion after wiring commercial policy into every layer of a system I fully control, with nobody to blame but myself.</p>
<p>Show me your pricing page, and I&#039;ll draw your database schema. The two always converge; they have no choice, because one is a set of promises and the other is where promises are kept. The only real question is whether anyone <em>designed</em> that convergence, or whether it just happened, one urgent pricing experiment at a time.</p>
<p>So, an exercise for your next architecture review: put the pricing page on the screen instead of the C4 diagram. Walk it boundary by boundary and ask where each promise is enforced.</p>
<p>If the room goes quiet somewhere between &quot;unlimited&quot; and the asterisk: congratulations. You&#039;ve just found your real architecture backlog ;)</p>
]]></content:encoded>
      <category>SaaS</category>
      <category>Software Architecture</category>
      <category>Pricing Strategy</category>
      <category>Multi-Tenancy</category>
      <category>Product Management</category>
    </item>
    <item>
      <title>Anti-Personas: The Users I Refuse to Serve</title>
      <link>https://bfrackowiak.pl/blog/anti-personas-the-users-i-refuse-to-serve/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/anti-personas-the-users-i-refuse-to-serve/</guid>
      <pubDate>Mon, 16 Mar 2026 09:00:00 +0100</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>The most important architecture document in my side project contains no architecture. It&#039;s a list of people I will never build for.</description>
      <content:encoded><![CDATA[<p>In the vision document of my latest side project (a self-hostable scheduling tool written in Go) there is a section I&#039;m prouder of than any diagram I&#039;ve drawn this year. It comes right after the personas, and it&#039;s titled <strong>anti-personas</strong>: the users this product will never serve. Named, described, and rejected before the first line of code existed.</p>
<p>It sounds negative. It&#039;s the most constructive thing in the repository.</p>
<h2>The Feature That Is Absence</h2>
<p>The tool itself is deliberately small: a single binary, SQLite by default, no Docker required, a hard budget of about five thousand lines of Go, and eleven ADRs explaining the boring choices. It is not trying to compete with Calendly or Cal.com. They have funding, teams, and ecosystems. It competes with their <em>narrow case</em>: one professional, one website, one booking flow they want to own end-to-end.</p>
<p>Every absence in that description is a decision, not a gap. No team features means no roles-and-permissions tree, no admin screens, no invitation flows. No billing integration means no webhook forest and no PCI anxiety. No video conferencing means no vendor SDK whose breaking changes become my weekend.</p>
<p>The absences even select the audience. The secondary persona in the vision doc is the senior engineer who self-hosts everything and will only adopt a tool that stays small. For them, &quot;no Docker required&quot; is not a limitation. It&#039;s the pitch.</p>
<p>People read a feature list to learn what a product does. But <strong>a feature list tells you what a product does; the anti-feature list tells you what it <em>is</em>.</strong> Constraints are the taste. Anyone can add; the identity lives in what you refuse.</p>
<h2>Rejection, Written Down</h2>
<p>The anti-personas are specific, and that specificity is the whole trick. A fifty-person sales team needing round-robin assignment: not our user. A recruiting org wanting in-app video interviews: not our user. Anyone hoping for a free tier with an upgrade path: there is no business model, this is a tool, so also not our user.</p>
<p>These are real people with valid needs, and they will happily pay somebody. They are simply <em>someone else&#039;s</em> users. Writing that down feels almost rude, which tells you exactly how rarely we do it.</p>
<p>Watch what each rejection buys. Kill round-robin and you never implement availability-merging across calendars, fairness algorithms, or the admin UI to configure them, plus every bug report at the intersection of all three. <strong>One anti-persona kills an entire tree of future edge cases before it sprouts.</strong> The cheapest code review is the one for code that never got written.</p>
<p>And feature triage becomes almost mechanical. Every incoming idea (mine included, at 2 a.m., when everything seems like a good idea) answers one question: <em>which persona is this for?</em> If the answer is an anti-persona, it&#039;s a clean no. Painlessly. Nothing personal; it&#039;s in writing.</p>
<h2>The Corporate Mirror</h2>
<p>This is where the side project starts explaining the day job.</p>
<p>In <a href="/blog/mathematicians-vs-lawyers/">Mathematicians vs. Lawyers</a> I described the edge-case explosion: specification documents swelling to hundreds of cases that nobody reads in full. I blamed corporate culture&#039;s inability to say no. The scheduler taught me the same lesson from the other side. The explosion doesn&#039;t happen because anyone is careless. It happens because <strong>a product company cannot refuse anyone.</strong> Every exception is revenue. Every &quot;no&quot; needs a VP&#039;s spine behind it. Every accepted special case is one more branch in the system, and no incentive structure on earth rewards the person who turned down money.</p>
<p>A solo project can refuse everyone. That is its superpower: not the tech stack, not the single binary, not the artisanal ADRs. The economics of &quot;no&quot; are simply different. At home, refusing costs nothing; at work, it costs a fight.</p>
<p>Which is exactly why organizations shouldn&#039;t dismiss this as hobby-scale wisdom. Two practices copy over directly. First, <strong>anti-personas as first-class strategy artifacts</strong>: written, named, and signed by someone with authority, so that &quot;we don&#039;t serve this segment&quot; is a document, not a rumor that dies in the next QBR. Second, <strong>absence-of-feature as a decision with an owner</strong>: an ADR for the thing you deliberately didn&#039;t build and why, so the next PM inherits the reasoning instead of innocently reopening the trapdoor.</p>
<p>I&#039;ve watched million-euro complexity enter systems through the simple absence of a written &quot;no&quot;. The document would have cost an afternoon.</p>
<h2>Practicing &quot;No&quot; Where It&#039;s Free</h2>
<p>My private conclusion is this: the ability to say no is the scarcest architectural resource I know, scarcer than senior engineers, scarcer than budget. And like any scarce skill, it atrophies without reps.</p>
<p>Side projects are my gym for refusal. Every anti-persona I write at home, every feature I decline with a one-line reference to the vision doc, is a rep. And those reps are what keep the diplomatic, watered-down, corporate-compatible version of my &quot;no&quot; from collapsing entirely when a stakeholder leans on it.</p>
<p>So, two questions to leave you with. Does your product have anti-personas? And if you wrote them down tomorrow, who in your organization would dare to sign the document?</p>
]]></content:encoded>
      <category>Product Strategy</category>
      <category>Software Architecture</category>
      <category>Side Projects</category>
      <category>Product Management</category>
      <category>Minimalism</category>
    </item>
    <item>
      <title>Edge Cases Written by Parliament</title>
      <link>https://bfrackowiak.pl/blog/edge-cases-written-by-parliament/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/edge-cases-written-by-parliament/</guid>
      <pubDate>Mon, 02 Mar 2026 09:00:00 +0100</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>What integrating with mandatory government systems taught me about specs you cannot refactor.</description>
      <content:encoded><![CDATA[<p>The strictest product owner I have ever worked with has never attended a standup, ignores my roadmap entirely, and publishes requirements as versioned PDFs. It&#039;s the state.</p>
<p>Building Desta, my invoicing platform for the Polish market, means integrating with three government systems: KSeF (the mandatory national e-invoicing API), the public company registry with its SOAP interface, and the JPK unified tax files. A quick disclaimer before we start: this is an engineering story, not tax advice. Please keep paying your accountant ;)</p>
<h2>The Spec You Cannot Refactor</h2>
<p>Tax law is an upstream dependency with no negotiation channel. You cannot file an issue against a statute. You cannot suggest a breaking change be batched into the next major version. You implement what the paragraph says, and the paragraph doesn&#039;t care about your domain model.</p>
<p>Two examples that changed how I think about requirements.</p>
<p>First: in KSeF, <strong>the XML is the legal invoice.</strong> The PDF (the thing every user believes is &quot;the invoice&quot;) is legally just a <em>visualization</em>, and has to be labeled as one. Think about what that does to your architecture: the artifact your users see and print is a derived view; the artifact almost nobody reads is the source of truth. Get the relationship backwards and you&#039;ve built a very pretty bug.</p>
<p>Second: invoice rows with certain VAT exemptions must render the VAT column <em>blank</em>. Not &quot;0.00&quot;. Not a dash. Blank. <strong>A one-pixel difference that is a legal requirement, not a style choice.</strong> There is no elegant abstraction that makes this go away; there is only encoding it correctly and pinning it with tests named after tax scenarios.</p>
<p>I wrote in <a href="/blog/mathematicians-vs-lawyers/">Mathematicians vs. Lawyers</a> about two thinking archetypes: the modelers who dissolve edge cases by reshaping the model, and the rule-thinkers who enumerate them. Well, this is the domain where the Lawyer archetype simply wins. Every edge case here has a paragraph number attached. My instinct is to find the model that makes exceptions disappear; in compliance work, the exceptions <em>are</em> the model.</p>
<h2>SOAP, Sessions, and the State</h2>
<p>Now the part everyone loves to mock: the technology.</p>
<p>The company registry speaks SOAP in 2026. The session ID travels in a custom HTTP header, not a cookie. That detail alone costs every integrator an afternoon of their life. The field for a building number has a bureaucratic Polish name that no autocomplete will ever guess. The documentation is a versioned PDF.</p>
<p>The inconvenient truth: <strong>it all works.</strong> The same requests produce the same responses, year after year. My integration code for that registry is the least-touched code I own.</p>
<p>KSeF authentication is a small choreography: fetch the public key, request a challenge, encrypt your token and a timestamp with RSA, redeem the result for a session. Is it elaborate? Yes. Is it security theater? Partly, perhaps. But it&#039;s <em>documented</em> choreography. You implement it once, wrap it, and forget it exists.</p>
<p>That word, &quot;wrap&quot;, is the architectural lesson. This is where the anti-corruption layer stops being a conference slide and starts earning its keep. The government&#039;s model, its vocabulary, its SOAP envelopes and bureaucratic field names get translated at the boundary, once, in one place. My domain never learns what the ministry calls a building number. The state stays outside; the model stays clean.</p>
<h2>Lessons for Architects Raised on REST</h2>
<p>If your entire career has been JSON over HTTPS with a <code>v2</code> in the path, integrating with the state is a masterclass in things our industry forgot.</p>
<p><strong>Stability is a feature, arguably the feature.</strong> These APIs change less per decade than a typical startup API changes per quarter. Deprecations are announced by legislation, with timelines measured in years. Meanwhile I&#039;ve integrated with modern SaaS APIs that shipped three breaking changes in a year, each announced in a changelog nobody reads.</p>
<p><strong>Compliance is a bounded context.</strong> It has its own language (the statute&#039;s, not yours), its own tests (named after tax scenarios, not user stories), and its own release rhythm: the calendar of the state, not your sprint. Treating it as &quot;just another feature&quot; is how compliance logic ends up smeared across a codebase.</p>
<p><strong>The reference implementation is the real spec.</strong> The ministry publishes reference client SDKs, and reading them is archaeology. You dig through layers to learn what the API <em>actually</em> expects, as opposed to what the PDF implies. Archaeology beats guessing. I made a similar argument in <a href="https://medium.com/@bfrackowiak/stop-calling-it-legacy-code-02308db28cb2">Stop Calling It Legacy Code</a>: old code is a knowledge container. Government reference code is the same idea at civilizational scale: decades of edge cases, fossilized and readable.</p>
<h2>Ugly, Documented, Stable</h2>
<p>Complaining about government APIs is the cheapest joke in engineering, and I&#039;ve told it myself. But after a year of running production integrations against them, I have to report something embarrassing: I trust these contracts more than most microservices I&#039;ve met in my career.</p>
<p>They are ugly. They are documented. They are stable. <strong>Two of those three qualities are the ones that actually matter</strong>, and they&#039;re not the one we keep optimizing for.</p>
<p>So, a question for your next architecture review: which of your internal APIs would survive being frozen for five years? Because the ministry&#039;s would. And did.</p>
]]></content:encoded>
      <category>Software Architecture</category>
      <category>Government Technology</category>
      <category>Domain-Driven Design</category>
      <category>Legacy Systems</category>
      <category>E-Invoicing</category>
    </item>
    <item>
      <title>ADRs for an Audience of One</title>
      <link>https://bfrackowiak.pl/blog/adrs-for-an-audience-of-one/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/adrs-for-an-audience-of-one/</guid>
      <pubDate>Mon, 16 Feb 2026 09:00:00 +0100</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>My hobby project has eleven Architecture Decision Records and zero users. That ratio is correct.</description>
      <content:encoded><![CDATA[<p>On my disk lives a small self-hosted scheduling tool I&#039;m building in Go. Single binary, SQLite by default, no Docker required, deliberately single-user. It has no users yet. Not even me, technically, since it isn&#039;t deployed. It does, however, have eleven Architecture Decision Records.</p>
<p>Eleven ADRs. Zero users. Colleagues have laughed at that ratio, and I get it: writing formal decision documents for a project whose entire engineering department is one guy in the evenings looks like corporate cosplay. I&#039;m going to argue the opposite: <strong>that ratio is correct</strong>, and the reasoning applies far beyond hobby projects.</p>
<h2>The Stakeholder with Amnesia</h2>
<p>The awkward truth about solo projects: you are not one person. You are a sequence of people wearing the same face, and they don&#039;t talk to each other.</p>
<p>There&#039;s the me who chose the chi router over the more popular frameworks. The me who decided SQLite is the default and Postgres just an adapter behind an interface. The me who picked sessions plus TOTP instead of wiring in an identity provider. Each of those people had context: benchmarks skimmed, trade-offs weighed, constraints that were obvious <em>that week</em>.</p>
<p>And then there&#039;s future-me. Future-me is a genuine stakeholder, one with amnesia. He opens the repo after three months away, finds a decision he doesn&#039;t remember making, and immediately starts distrusting it. <em>Why is there no ORM here? Was I lazy, or was I smart?</em> Without a record, he cannot tell. And an engineer who distrusts his own codebase starts rewriting things that were never broken.</p>
<p>The economics are absurdly lopsided. An ADR written on the day of the decision costs ten minutes. Reconstructing the reasoning a year later costs an afternoon, and the reconstruction is usually wrong, because you rebuild yesterday&#039;s decision out of today&#039;s knowledge. Eleven ADRs before the project is even feature-complete isn&#039;t overhead. <strong>It&#039;s the project&#039;s memory</strong>, and it&#039;s the only memory that survives the gap between evening sessions.</p>
<h2>Writing It Down Changes What You Decide</h2>
<p>The bigger surprise wasn&#039;t that ADRs help me remember. It&#039;s that they changed the decisions themselves.</p>
<p>Every ADR template carries one quietly brutal section: <em>alternatives considered</em>. You cannot fill it honestly without laying the options side by side, in daylight. And in that daylight, a certain kind of decision dies: the resume-driven one. Try writing &quot;Alternative B: the boring technology that fits the problem perfectly. Rejected because: I wanted to play with Alternative A&quot;, then signing your name under it. The template forces the question every architect should ask and rarely does: <strong>do I want this technology, or does the problem?</strong></p>
<p>Two more effects I didn&#039;t expect:</p>
<ul>
<li><strong>Timestamped context.</strong> An ADR records what was true and known at decision time. When the world changes (and it will), the record protects past-you from unfair judgment, and tells present-you exactly which assumption expired.</li>
<li><strong>Deferred decisions stop haunting you.</strong> One of my eleven ADRs decides <em>not to decide</em>: a query-codegen library that looks attractive, postponed until the hand-written SQL layer actually hurts, with a written trigger for revisiting. Before ADRs, that would have been ambient anxiety, a tab left open in my head. Now it&#039;s a documented &quot;not yet&quot; with an alarm attached.</li>
</ul>
<p>There&#039;s a wry pleasure in noticing that the discipline works precisely because nobody is watching. No committee to perform for, no reviewer to impress. Just the decision and me, on paper ;)</p>
<h2>From Hobby Repo to Boardroom</h2>
<p>Now scale it up, because this is where it stops being a solo-dev quirk.</p>
<p>An ADR log plus one named decider is <strong>the cheapest governance stack that exists</strong>. No tooling, no process framework, no steering committee. A folder of text files and a name. I&#039;ve written elsewhere about how organizations design their systems obsessively while leaving the decisions about those systems undesigned (<a href="/blog/decisions-have-an-architecture-too/">Decisions Have an Architecture Too</a>). The ADR is the smallest possible fix. It doesn&#039;t make decisions faster. It makes them <em>exist</em>, in a form that can be questioned, inherited, and defended.</p>
<p>When I want to judge the health of a team, I ask for their decision log before I read a line of their code. A living log full of honest alternatives tells me more than any maturity assessment. An empty one tells me the architecture lives in somebody&#039;s head, and heads get promoted, poached, and burned out.</p>
<p>And there&#039;s a reason for this practice that didn&#039;t exist when the ADR format was invented: <strong>AI agents</strong>. Agents read our repositories now. Code tells them <em>what</em> the system does; only decision records tell them <em>why</em>: which constraints were real, which paths were rejected and on what grounds. Decision reasoning is precisely the thing a model cannot infer from source, no matter how good it gets. My eleven small markdown files turn out to be premium context for every future agent (and every future human) working in that repo.</p>
<h2>An Audience of One</h2>
<p>So yes: eleven ADRs, zero users, and I&#039;ll write the twelfth tonight without a trace of embarrassment.</p>
<p>Over the years I&#039;ve arrived at a personal rule, and my side projects are where I enforce it at full strength, precisely because nobody&#039;s watching: <strong>if a decision isn&#039;t worth writing down, it wasn&#039;t a decision. It was a mood.</strong></p>
<p>Moods are fine. I&#039;ve shipped plenty of good code on moods. But a mood can&#039;t be reviewed, can&#039;t be inherited, and can&#039;t defend itself six months later.</p>
<p>A small experiment before you go: open your most important repository and try to find where its five biggest decisions are written down. Not the code that implements them, but the reasoning that produced them.</p>
<p>If you can&#039;t find it, ask yourself who it&#039;s stored in instead.</p>
<p>And what exactly is your plan for the day they hand in their notice?</p>
]]></content:encoded>
      <category>Architecture Decision Records</category>
      <category>Software Architecture</category>
      <category>Side Projects</category>
      <category>Engineering Practices</category>
      <category>Documentation</category>
    </item>
    <item>
      <title>Questions Are an Architect&#039;s Sharpest Tool</title>
      <link>https://bfrackowiak.pl/blog/questions-are-an-architects-sharpest-tool/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/questions-are-an-architects-sharpest-tool/</guid>
      <pubDate>Mon, 26 Jan 2026 09:00:00 +0100</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>The highest-leverage artifact I produce is not a diagram, not a decision record, not a slide deck. It&#039;s a list of questions.</description>
      <content:encoded><![CDATA[<p>The best architecture review I can remember produced no decisions, no action items, and one question. Someone looked at a beautifully complete design for an aggregation service and asked: &quot;Who owns this number after it&#039;s published?&quot; Silence. Two weeks later the design was different, and better, in ways no amount of diagram polishing would have achieved.</p>
<p>I&#039;ve been collecting that silence ever since.</p>
<p>Early in my career I believed seniority meant having answers: fast, confident, preferably with a diagram. A dozen years in, I measure my weeks differently: by the quality of the questions I managed to ask before the answers got expensive. Private observation, not science, as usual. But this one I&#039;d defend in any review.</p>
<h2>The question list is a deliverable</h2>
<p>Every solution design I ship travels with a companion artifact: an open-questions file. Beyond that, I maintain separate documents with titles like &quot;architecture questions to product&quot;. They are versioned, statused (open, answered, overtaken by events), and reviewed with the same seriousness as any diagram. Colleagues occasionally find this odd. Aren&#039;t architects paid for answers?</p>
<p>Here&#039;s the asymmetry that convinced me otherwise. <strong>Answers age badly.</strong> An answer encodes the constraints of the day it was written: the team&#039;s size, the vendor&#039;s pricing, the traffic profile, what the organization believed that quarter. Constraints rot silently, but the answer keeps getting repeated, confidently, like a cached value nobody thought to invalidate. Some of the most dangerous sentences in engineering start with &quot;we already decided that.&quot;</p>
<p>Good questions do the opposite: <strong>they compound.</strong> &quot;Who owns this after go-live?&quot; produces fresh truth every single time you re-ask it. &quot;What happens when these two sources disagree?&quot; outlives reorgs, migrations, and three generations of diagrams. An answer is a snapshot. A good question is a probe you can re-run.</p>
<p>That&#039;s why the question list deserves version control. It is the only artifact I produce whose value reliably <em>grows</em> with time.</p>
<h2>Two kinds of questions</h2>
<p>Now the craft, because not everything with a question mark is a tool.</p>
<p>The first kind <strong>exposes hidden assumptions</strong>. &quot;What must be true for this design to work?&quot; &quot;Who pays when this fails at 2 a.m.?&quot; &quot;Which of these arrows is a wish and which is a contract?&quot; These questions are cheap before the design is approved and brutal after go-live. Their entire value is in the timing. They tend to be short, slightly uncomfortable, and impossible to answer with a framework name.</p>
<p>The second kind <strong>performs seniority</strong>. The gotcha delivered for the audience. The technology flex (&quot;have you considered Kafka?&quot;) asked not to learn but to be seen knowing Kafka exists. The fifteen-minute &quot;question&quot; that is actually a speech with rising intonation. This kind produces no design changes, only defensive meetings.</p>
<p>My test is simple: would I still ask this question in a one-on-one, with no witnesses? If yes, it&#039;s a probe. If it only makes sense with spectators, it&#039;s theater. I&#039;ve written before about how much of corporate life already is (<a href="/blog/data-driven-theater/">Data-Driven Theater</a>).</p>
<p>One more piece of craft: a question without an owner and a check-back date is not a tool, it&#039;s a sigh. &quot;Someone should clarify this&quot; has never once, in my experience, resulted in someone clarifying anything.</p>
<h2>Open questions as an honesty mechanism</h2>
<p>The deepest value of the question list is what it does to trust.</p>
<p>Every solution design of mine ends with an &quot;Open Questions&quot; section: the things I don&#039;t know, stated before someone else discovers them for me. It felt exposing the first few times. It turned out to be disarming. A review of a &quot;complete&quot; design is a hunt: reviewers win by finding holes. A review of a design with three declared holes is a search party: the reviewers join you, because you&#039;ve already conceded the interesting part (that holes exist) and named where you think they are.</p>
<p>Declared uncertainty, I keep relearning, builds more credibility than performed certainty ever did.</p>
<p>Years ago, in <a href="/blog/technical-leader-identity-disorder/">Technical Leader — Identity Disorder</a>, I leaned on Socrates while untangling my own role. I&#039;ve since stopped treating &quot;I know that I know nothing&quot; as philosophical decoration. It&#039;s a working method, and the open-questions file is its production implementation. Confidence is not the absence of open questions. <strong>Confidence is knowing exactly which questions are open.</strong></p>
<p>Which is why an <em>empty</em> Open Questions section in a big design is, for me, a red flag with a siren attached. If you can&#039;t name three things you don&#039;t know about your own architecture, that&#039;s not completeness. That&#039;s the first open question.</p>
<h2>Try it</h2>
<p>A small experiment for your next design document. Add an &quot;Open Questions&quot; section with at least three honest entries, each with an owner and a date to check back. Then watch what the review becomes.</p>
<p>And a question to leave you with, naturally. What&#039;s the best question anyone ever asked about your design, the one that stung for a second and then saved you a quarter of rework? That&#039;s the sharpest tool in our trade. Collect those. Sharpen yours.</p>
]]></content:encoded>
      <category>Software Architecture</category>
      <category>Solution Design</category>
      <category>Critical Thinking</category>
      <category>Engineering Leadership</category>
      <category>Communication</category>
    </item>
    <item>
      <title>The Anti-Corruption Layer Is for People Too</title>
      <link>https://bfrackowiak.pl/blog/the-anti-corruption-layer-is-for-people-too/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/the-anti-corruption-layer-is-for-people-too/</guid>
      <pubDate>Mon, 19 Jan 2026 09:00:00 +0100</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>My favorite pattern from Domain-Driven Design protects codebases from foreign models. I keep prescribing it to departments.</description>
      <content:encoded><![CDATA[<p>Ask an architect for their favorite pattern and you&#039;ll usually hear something impressive: event sourcing, CQRS, cells. Mine is humbler: <strong>the anti-corruption layer</strong>. A boring translation shim from the Domain-Driven Design toolbox. I love it because it&#039;s the only pattern I know that works equally well on an integration diagram and on an org chart.</p>
<p>Usual disclaimer: what follows comes from years of private observations at integration boundaries, not from scientific literature. The boundaries were real, though.</p>
<h2>What the layer actually protects</h2>
<p>A quick refresher for the non-DDD crowd. An anti-corruption layer sits between your system and an upstream system whose model you do not control: a legacy platform, a vendor API, another team&#039;s service. Its job is translation: their concepts arrive, get mapped into <em>your</em> language at the border, and only then touch your domain.</p>
<p>The name is the important part. Evans didn&#039;t call it &quot;adapter&quot; or &quot;facade&quot;. He called it <em>anti-corruption</em>, because that is what it prevents. And here is what most teams get wrong: <strong>the layer protects your model&#039;s integrity, not your API.</strong> The danger was never that a remote call fails; you have retries for that. The danger is that their <code>status = 7</code>, their nullable-everything contract, their subtly different definition of &quot;customer&quot; starts living inside your entities. You import one convenient enum today, and six months later your code speaks two languages, neither of them fluently. Every feature after that pays the tax.</p>
<p>Is the extra hop always worth it? Honestly, no. Translation costs: another mapping class, more tests, and a guaranteed &quot;why this indirection?&quot; comment in code review. If the upstream model is stable, close to yours, and read-only, map it directly and move on. The layer earns its keep when the upstream model is volatile, when its language contradicts yours, or when you catch yourself sprinkling <code>if (theirType == LEGACY_B)</code> across your domain logic. That <code>if</code> is not a smell of future corruption. It <em>is</em> the corruption, already inside.</p>
<h2>Semantic corruption, human edition</h2>
<p>Now watch the same mechanism run on people.</p>
<p>Teams import each other&#039;s vocabularies exactly the way codebases import foreign enums: silently and wholesale. The channel isn&#039;t an API; it&#039;s a shared spreadsheet, a copied ticket, a slide lifted from another department&#039;s deck. A word walks across the boundary (&quot;account&quot;, &quot;assessment&quot;, &quot;active&quot;) carrying someone else&#039;s model inside it, and nobody inspects the luggage.</p>
<p>I&#039;ve written before that most &quot;data-driven&quot; fights are really collisions between semantic contracts nobody wrote down (<a href="/blog/data-driven-theater/">Data-Driven Theater</a>). This is how those contracts get broken in the first place. A term optimized for sales, with sales&#039; compromises baked in, becomes an engineering domain term. A status that conflates two different situations gets copied into three more teams&#039; backlogs. The breakage replicates through the organization precisely like corrupted records through an unvalidated pipeline.</p>
<p>Nobody decides any of this. That is the point. <strong>Corruption rarely announces itself; it arrives one borrowed word at a time.</strong></p>
<h2>Translate or unify, but never &quot;just align&quot;</h2>
<p>So what do you do at a boundary, human or technical? You have exactly two honest options.</p>
<p><strong>Adopt a genuinely shared model</strong> when the concept is core to both sides and you are prepared to co-own its evolution. This is the ubiquitous-language dream, and it is expensive: shared definitions need governance, a change process, and someone empowered to say no. Reserve it for the handful of concepts that truly deserve it. This is model work (relations, constraints, evolution), the level I argued architects should stay at in <a href="/blog/mathematicians-vs-lawyers/">Mathematicians vs. Lawyers</a>.</p>
<p><strong>Translate at the boundary</strong> when the other side&#039;s model is legitimately <em>theirs</em>: owned by them, stable, optimized for their world. Keep a glossary. Better yet, keep a translator: a person who consciously maintains the mapping. &quot;Their &#039;client&#039; is our &#039;buyer&#039;, except during trials.&quot; A named, human anti-corruption layer. It feels like bureaucracy and costs one page of documentation. It saves quarters of confusion.</p>
<p>The trap is the third option, the one everyone reaches for first: <strong>&quot;let&#039;s just align the teams.&quot;</strong> It sounds like the cheap solution. It is the most expensive item on the menu, because alignment is not a meeting. It is permanent joint ownership of a language. Every future change to the concept now requires both parties, forever, including the parties who will inherit it after the next reorg. Most teams that &quot;aligned&quot; in a two-hour workshop actually just adopted whichever vocabulary was spoken loudest, imported its bugs along with it, and called the result consensus.</p>
<p>Choose deliberately: unify what is core, translate what is foreign, and never leave a boundary unmanaged just because the two teams happen to like each other. Friendship is not a data contract.</p>
<h2>The audit</h2>
<p>A small exercise for your next quiet Friday. Pick the three most central words on your team&#039;s diagrams. For each one, ask: where did this word come from? Does it mean the same thing one department over? And if it doesn&#039;t, who is doing the translation?</p>
<p>If the answer to the last question is &quot;nobody&quot;, congratulations. You have just located the exact spot where your model is importing someone else&#039;s bugs, free of charge, on a daily schedule.</p>
<p>Which word in your domain has a foreign accent? And do you remember deciding to let it in?</p>
]]></content:encoded>
      <category>Domain-Driven Design</category>
      <category>Software Architecture</category>
      <category>Team Communication</category>
      <category>Organizational Culture</category>
      <category>Software Engineering</category>
    </item>
    <item>
      <title>The Migration That Never Starts</title>
      <link>https://bfrackowiak.pl/blog/the-migration-that-never-starts/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/the-migration-that-never-starts/</guid>
      <pubDate>Mon, 12 Jan 2026 09:00:00 +0100</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>Every organization has one. Yours does too. It has a folder, a deck, and a birthday.</description>
      <content:encoded><![CDATA[<p>Somewhere in your company&#039;s document storage there is a folder. It might be called &quot;Migration Options,&quot; or &quot;Target Architecture,&quot; or &quot;Platform Modernization Analysis.&quot; It contains several well-written documents, each comparing three to five options, each ending with &quot;recommendation to be validated.&quot; The oldest file is years old. The newest was added last quarter.</p>
<p>I have contributed to such folders. I have <em>created</em> such folders. So take what follows as private observations, not science, but observations from both sides of the crime scene ;)</p>
<p>Here is the conclusion those folders eventually forced on me: <strong>organizations don&#039;t fail migrations. They fail to start them.</strong> The industry loves horror stories about big migrations that went wrong. Honestly, I&#039;ve seen far more damage done by migrations that never left PowerPoint.</p>
<h2>The Options-Collection Ritual</h2>
<p>The mechanism looks thoroughly respectable. A migration is clearly needed: the old platform hurts, everyone agrees, nobody objects. So we do the responsible thing: we analyze. We produce a comparison matrix. Options, strengths, weaknesses, costs, risks.</p>
<p>Then the quarter ends, priorities wobble, and the document goes to the folder. Next quarter someone reasonably notes that &quot;the landscape has changed&quot; (the landscape has always changed) and commissions a refresh. A new document is added. <strong>Nothing is ever subtracted.</strong></p>
<p>This is analysis as a socially acceptable form of postponement. Nobody ever got fired for producing a comparison matrix. Producing one <em>feels</em> like progress, reads like diligence in a status report, and carries none of the personal risk of an actual decision. The ritual has all the ceremonial properties of work (effort, artifacts, meetings) with none of the consequences.</p>
<p>And every refresh quietly resets the clock. You can postpone anything indefinitely if you re-analyze it at the right frequency.</p>
<h2>Decision Debt</h2>
<p>We track technical debt in backlogs. We track risks in registers. The compounding cost of <em>not deciding</em> is tracked exactly nowhere, and it&#039;s often the largest item on the invisible balance sheet.</p>
<p>Because while the decision waits, the old platform doesn&#039;t wait with it. Every sprint adds new integrations to the thing you intend to leave. Every new feature is another consumer of the legacy contract, another strand in the net that will have to be cut later. The migration doesn&#039;t get cheaper while you analyze it. The price of &quot;not yet&quot; is invoiced monthly and itemized never.</p>
<p>There&#039;s a human line item too, and it compounds faster than the technical one. The third time a migration is announced and doesn&#039;t start, teams stop believing announcements. Engineers who prepared for the move quietly file the experience under &quot;management noise.&quot; The zombie migration doesn&#039;t just burn analysis budget; it devalues every future commitment leadership makes. That currency does not come back at the next all-hands.</p>
<h2>One-Way Doors, Two-Way Doors</h2>
<p>Amazon popularized a genuinely useful question: is this decision a one-way door or a two-way door? Irreversible decisions deserve slow, heavy scrutiny. Reversible decisions deserve speed; the cost of a wrong reversible decision is just the walk back through the door.</p>
<p>Now look honestly at a migration. Routing one endpoint through the new service: reversible. Dual-writing to both data stores: reversible by design. Moving one read path, one team, one low-risk workload: two-way doors, all of them. The overwhelming majority of migration <em>steps</em> can be undone with a config change and a mildly embarrassing standup update.</p>
<p>Yet the organization treats every step as a bet-the-company moment. Why? Because the migration was framed as one giant decision, &quot;we are moving to X&quot;, and <em>that</em> framing is a one-way door, socially if not technically. Nobody wants to personally own the giant door, so everybody keeps studying the hinges.</p>
<p>The trick is almost insultingly simple: stop deciding the migration. Decide the next step. <strong>Decompose until every step is a two-way door, then walk through the first one.</strong></p>
<h2>What Actually Unblocks</h2>
<p>Three things, in my experience, and none of them is another analysis:</p>
<ol>
<li><strong>A timebox on the analysis.</strong> Options expire like milk. Decide by a date, not by certainty, because certainty is not coming. It never was. An 80%-informed decision this quarter beats a 95%-informed decision that will never be made.</li>
<li><strong>A named decider.</strong> Not a committee, not &quot;alignment,&quot; one accountable person. Consensus is where migrations go to die; a name is where they wake up. I&#039;ve written more about decision rights in <a href="/blog/decisions-have-an-architecture-too/">Decisions Have an Architecture Too</a>.</li>
<li><strong>A first step too small to refuse.</strong> One endpoint, one consumer, two weeks, fully reversible. Its job is not impact. Its job is to convert the migration from a document into a fact, because facts attract budget, volunteers, and momentum in a way folders never do.</li>
</ol>
<p>A failed migration at least produces knowledge: you learn where it breaks, what it costs, who steps up. A migration that never starts produces only documents, plus a slow, expensive lesson in how little your organization&#039;s announcements are worth.</p>
<p>So, one honest question: which folder in your document storage is quietly approaching its fifth birthday?</p>
<p>Maybe don&#039;t refresh it this quarter. Maybe pick the smallest reversible step it describes and just open door number one.</p>
]]></content:encoded>
      <category>Software Architecture</category>
      <category>Decision Making</category>
      <category>Legacy Systems</category>
      <category>Engineering Leadership</category>
      <category>Organizational Culture</category>
    </item>
    <item>
      <title>Diagrams Are Conversations, Not Documentation</title>
      <link>https://bfrackowiak.pl/blog/diagrams-are-conversations-not-documentation/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/diagrams-are-conversations-not-documentation/</guid>
      <pubDate>Mon, 05 Jan 2026 09:00:00 +0100</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>The most useful diagram of my career was ugly, partially wrong, and deleted within a week.</description>
      <content:encoded><![CDATA[<p>Early in my architecture career I spent three evenings polishing a system diagram. Aligned boxes. Consistent arrow styles. A legend. Color semantics. I presented it to the room, people nodded, someone said &quot;great overview,&quot; and the meeting moved on. That diagram changed exactly nothing, and I was very proud of it.</p>
<p>A few years later I stood at a whiteboard with a marker I had to shake to make work, drew four crooked boxes, and before I finished the fifth a senior developer interrupted: &quot;That&#039;s not how it flows. The events go through the queue first.&quot; Twenty minutes of arguing later, we had discovered that two teams held two different mental models of the same integration, and had been quietly building against both.</p>
<p>The crooked boxes did more for the architecture than the three-evening masterpiece ever did. It took me embarrassingly long to understand why, so let me save you the time: <strong>a diagram&#039;s value is consumed in the meeting where it is drawn.</strong> What survives the meeting is not an asset. It&#039;s a receipt.</p>
<p>Usual disclaimer applies: private observations from one architect&#039;s practice, not science.</p>
<h2>The Half-Life Problem</h2>
<p>Polished enterprise-architecture diagrams are dead on arrival. Not because they&#039;re wrong on day one, but because the system starts drifting away from them on day two, and no contract of maintenance was ever signed. Everyone wants diagrams to exist; nobody has &quot;update the diagrams&quot; in their sprint.</p>
<p>Run this audit in your own organization: ask a team to show you their &quot;current state&quot; diagram, then watch the eyes. The honest answer is always some flavor of &quot;current as of the workshop we ran in spring.&quot; We don&#039;t version diagrams. We hold funerals for them, quietly, by never opening the file again.</p>
<p>Meanwhile the messy whiteboard photo from that one heated session keeps getting reposted in chat threads for months. Not because the sketch is good (it objectively isn&#039;t). Because everyone who was in the room remembers the argument it hosted. The picture is a bookmark for a shared understanding. People who weren&#039;t there see boxes; people who were see the conversation.</p>
<h2>One Diagram, One Question</h2>
<p>The everything-diagram is the most common failure I see: one canvas trying to answer every question about the system, and therefore answering none. Fifty boxes, four zoom levels, a legend that needs its own legend.</p>
<p>My working rule is brutally simple: <strong>one diagram, one question.</strong> Scope every drawing to the single decision it must support. &quot;Should checkout call inventory directly or go through events?&quot; needs five boxes. It does not need your cluster topology, and it absolutely does not need the boxes that are only there because leaving them out felt impolite.</p>
<p>Anything that doesn&#039;t influence the decision is decoration, and decoration invites feedback about decoration. You wanted a call on the integration pattern; you got forty minutes on whether the auth service should also be pictured.</p>
<p>One small trick changes everything: title the diagram with the question, not the system. &quot;Where should risk aggregation live?&quot; starts a very different meeting than &quot;Platform Overview v7.&quot;</p>
<h2>Draw Live, Don&#039;t Present</h2>
<p>The pattern I trust most costs nothing: draw the diagram <em>in</em> the meeting, badly, in front of everyone.</p>
<p>A finished diagram triggers politeness; people approve documents. An unfinished diagram triggers honesty; people cannot stop themselves from correcting a sketch. And the moment someone says &quot;that arrow is backwards&quot; is the exact moment your architecture is being validated, for free, by the people who know the ground truth. There is no cheaper review process on the market.</p>
<p>I&#039;ll admit why I resisted this for years: presenting polished work is a way of performing competence. Drawing crooked boxes in public feels junior. It isn&#039;t. It takes considerably more seniority to be visibly unfinished in a room than to be privately perfect ;)</p>
<p>The practical version: come with the question written at the top, draw the first three boxes yourself, then hand over the marker, literally or rhetorically. &quot;What am I missing?&quot; outperforms &quot;any questions?&quot; every single time.</p>
<h2>The AI Twist</h2>
<p>And now the part that changes the economics of all of the above. AI agents will happily generate a C4 diagram, a sequence diagram, or a dependency graph from a paragraph of text: on demand, in seconds, regenerated whenever the code changes. The mechanical act of drawing, the thing that used to cost me three evenings, now costs approximately nothing.</p>
<p>Which exposes what was true all along: the scarce skill was never the drawing. <strong>The scarce skill is choosing what to show</strong>: which question, at which altitude, for which audience. An agent can render fifty boxes flawlessly; it cannot know that this particular room, on this particular Tuesday, needs exactly five of them and one uncomfortable arrow.</p>
<p>Diagram-as-artifact is deflating fast. Diagram-as-conversation is appreciating. Plan your skills portfolio accordingly.</p>
<p>So, a small experiment for your next design review: don&#039;t bring the finished diagram. Bring the question, an empty canvas, and the willingness to be corrected in public.</p>
<p>And if you want a sobering metric for your organization, try this one: when did a diagram last change a real decision here, and was it the polished one?</p>
]]></content:encoded>
      <category>Software Architecture</category>
      <category>Diagrams</category>
      <category>Communication</category>
      <category>Technical Leadership</category>
      <category>Artificial Intelligence</category>
    </item>
    <item>
      <title>Nobody Owns the Sum</title>
      <link>https://bfrackowiak.pl/blog/nobody-owns-the-sum/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/nobody-owns-the-sum/</guid>
      <pubDate>Mon, 22 Dec 2025 09:00:00 +0100</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>Every squad owns its piece. The customer buys the whole. Guess which one has no owner.</description>
      <content:encoded><![CDATA[<p>Some time ago I sat in a meeting where somebody asked a simple question: <strong>&quot;Who owns the aggregated risk score?&quot;</strong></p>
<p>Silence. Then everyone answered at once, each about their own piece. One squad owned the data ingestion. Another owned one of the sub-scores feeding the aggregate. A third owned the screen where the final number is displayed to the customer. The number itself (the thing customers actually look at, the thing sales sells and support gets called about) was owned by nobody.</p>
<p>It took three meetings to establish that. Not because anyone was hiding. Everyone genuinely believed someone else had it.</p>
<p>What follows is a pattern I&#039;ve seen too many times to ignore. Private observations, no study behind them, just scars.</p>
<h2>The Org Chart Produces Parts</h2>
<p>We assign ownership by component, because components map neatly onto teams. A squad gets a service, a database, a screen. Clean boundaries, clear accountability, everyone can draw their box. So far, so good.</p>
<p>But the most valuable outcomes an organization produces are not components. They are <strong>sums</strong>: the aggregated score, the end-to-end response time, the coherence of the user experience, the total cloud bill. A sum, by definition, crosses boxes. And whatever crosses boxes on an org chart falls between them.</p>
<p>Conway&#039;s law is usually quoted about system design: your architecture will mirror your communication structure. There&#039;s an ownership corollary that gets much less airtime: <strong>an org chart can produce parts, but it cannot produce wholes.</strong> Wholes need an owner that the chart does not naturally generate. Someone has to create that role on purpose.</p>
<p>Nobody plans an orphan. An orphan is simply what&#039;s left over after everyone finishes claiming what&#039;s theirs.</p>
<h2>How to Spot One</h2>
<p>Orphaned aggregates don&#039;t announce themselves. They surface as symptoms, and every symptom looks like something else:</p>
<ul>
<li><strong>The ping-pong ticket.</strong> A cross-squad bug bounces between teams, each proving, correctly, that their part behaves as specified. Every part worked; the sum was wrong. The ticket ages until a frustrated customer escalates it past all of you.</li>
<li><strong>The decision that waits months.</strong> Changing a whole requires a yes from everyone who owns a part. A yes from everyone is, in practice, a yes from no one. So the improvement idea circles the organization like luggage nobody claims.</li>
<li><strong>The invisible roadmap item.</strong> Aggregate improvements appear on no backlog, because no backlog hosts them. Each squad&#039;s roadmap is full, full of parts.</li>
<li><strong>The human failsafe.</strong> The only real &quot;owner&quot; is a veteran who remembers how the whole thing fits together. That&#039;s not ownership, that&#039;s memory, and it resigns when they do.</li>
</ul>
<p>If you recognize two or more of these, you&#039;re not looking at a process problem. You&#039;re looking at an orphan.</p>
<h2>The Architect&#039;s Move</h2>
<p>Where I&#039;ve landed on what a solution architect should actually do about it is less heroic than you might hope.</p>
<p><strong>First: name the orphan out loud.</strong> This sounds trivial and isn&#039;t. As long as the aggregate has no name, its non-ownership is invisible; there is literally nothing to point at. The moment you name it (&quot;the overall score&quot;, &quot;the end-to-end latency budget&quot;, &quot;the network aggregate&quot;) the question <em>who owns this?</em> stops being unaskable and becomes merely awkward. Awkward is progress.</p>
<p><strong>Second: force an explicit decision.</strong> Not make it. Force it. The architect&#039;s job is to put the organization in a position where <em>not deciding</em> is the visible, embarrassing option. Getting from &quot;we should discuss this&quot; to an actual named owner is a craft of its own. I wrote about that machinery separately in <a href="/blog/decisions-have-an-architecture-too/">Decisions Have an Architecture Too</a>.</p>
<p><strong>Third: accept that sometimes the answer is new.</strong> Occasionally an existing squad&#039;s charter genuinely grows to cover the sum. More often, an honest decision produces something that didn&#039;t exist before: a capability owner, a small platform team, a named service with a named human. That&#039;s not org bloat. It&#039;s the org finally matching what it sells.</p>
<p>And one warning from experience. The default answer in the room will be: <em>&quot;the architect can own it.&quot;</em> Refuse politely. I&#039;ve accepted a few orphans myself over the years; it flatters you for about a week, and then you discover you&#039;re the babysitter of an aggregate with no budget, no backlog, and no team. Architecture must not become the attic where the organization stores what it doesn&#039;t want to decide about.</p>
<p><strong>Components get owners; sums get hopes.</strong> The whole game is converting one hope at a time into an owner.</p>
<h2>Try It on Your Own System</h2>
<p>Here&#039;s a fifteen-minute exercise that costs nothing. Walk your value stream end to end, from the first customer click to the last invoice, and count the things that <em>everyone depends on and no one owns</em>. The aggregate score. The overall latency. The consistency of terminology across screens. The total cost of the pipeline.</p>
<p>My prediction: you&#039;ll find at least three. Every organization I&#039;ve looked at has them, because org charts are made of boxes and value is made of sums.</p>
<p>So the question I&#039;ll leave you with: <strong>which orphan in your organization is expensive enough that simply naming it would change next quarter&#039;s roadmap?</strong></p>
<p>Name it in the next meeting. Watch what happens ;)</p>
]]></content:encoded>
      <category>Software Architecture</category>
      <category>Organizational Design</category>
      <category>Conways Law</category>
      <category>Engineering Leadership</category>
      <category>Ownership</category>
    </item>
    <item>
      <title>Decisions Have an Architecture Too</title>
      <link>https://bfrackowiak.pl/blog/decisions-have-an-architecture-too/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/decisions-have-an-architecture-too/</guid>
      <pubDate>Mon, 15 Dec 2025 09:00:00 +0100</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>We design our systems obsessively, then let the decisions about them happen by loudest voice.</description>
      <content:encoded><![CDATA[<p>I once counted the workshops we had held about the same topic: who owns a shared access-control integration. Three. Three workshops, the same diagrams, largely the same people, and at the end of each one the warm feeling of a productive discussion and no decision whatsoever. The fourth workshop was already in the calendar.</p>
<p>The fourth workshop never happened. We assigned letters instead. More on the letters in a moment.</p>
<p>As architects we obsess over system design: boundaries, contracts, failure modes, who calls whom. Meanwhile the <em>decisions about those systems</em>, often worth more than the systems themselves, happen with no design at all. Whoever speaks loudest, or last, or closest to the deadline, wins. Usual disclaimer: what follows are private observations from inside one organization, not science.</p>
<h2>The Meeting That Cannot Decide</h2>
<p>Here&#039;s the anatomy of a stalled architecture decision, and I bet you&#039;ve lived it.</p>
<p>A question appears: who owns X, do we build Y, do we retire Z. A meeting is called. Smart people attend and say smart things. Somewhere near the end, someone summarizes &quot;so we&#039;re aligned&quot;, and everyone nods, each person having heard a slightly different decision. Two weeks later the topic is back, re-litigated from scratch by whoever wasn&#039;t in the room.</p>
<p>The root cause is almost never missing information. It&#039;s that <strong>nobody knows who actually decides.</strong> A decision made by a meeting belongs to no one, so it evaporates the moment the meeting ends. Un-deciding it costs nothing, because deciding it cost nothing.</p>
<p>We would never build a system where any service can silently overwrite another&#039;s data. Yet we run decision processes where any sufficiently senior passer-by can overwrite last month&#039;s conclusion.</p>
<h2>RAPID, in Plain Words</h2>
<p>The letters that saved us from workshop number four come from Bain &amp; Company: <strong>RAPID</strong> (their trademark, their consultants will be pleased I said so). Strip away the consulting varnish and it&#039;s simply an <strong>API contract for decision-making</strong>:</p>
<ul>
<li><strong>R: Recommend.</strong> The person who does the analysis and brings a concrete proposal. Not a vibe, a proposal.</li>
<li><strong>A: Agree.</strong> The few whose formal sign-off is required (security, compliance). A veto with named grounds, not a mood.</li>
<li><strong>P: Perform.</strong> Whoever will do the work. They get a voice <em>before</em> the decision, not a surprise after it.</li>
<li><strong>I: Input.</strong> People consulted because they know things. Consulted, not obeyed.</li>
<li><strong>D: Decide.</strong> One name. Not a committee, not a forum, not &quot;the leadership&quot;. One name.</li>
</ul>
<p>Five letters, one rule: every letter is a person, and D appears exactly once.</p>
<p>The magic is not the framework. It&#039;s what writing the letters down does to behavior. Our ownership dispute that survived three workshops was resolved in days once the letters existed. The R stopped selling to the whole room and wrote one decent document. The I&#039;s gave input without needing to &quot;win&quot;. And the D, suddenly aware the decision was <em>theirs</em> with their name on it, asked sharper questions in one meeting than the previous three had produced in total.</p>
<p>Nothing about the problem changed. Only the architecture of the decision did.</p>
<h2>The Anti-Patterns</h2>
<p>Once you have the vocabulary, you start seeing the broken decision architectures everywhere:</p>
<p><strong>Everyone has a veto.</strong> The A role inflated until half the org can block and nobody can approve. A system where every component can halt the pipeline and no component can ship it. The result isn&#039;t safety. It&#039;s decision starvation with extra meetings.</p>
<p><strong>Consensus theater.</strong> The meeting is formally seeking a decision but actually seeking unanimous comfort. The decision waits not for the best argument but for the last objection to get tired. I&#039;ve written about the staged version of data-driven meetings in <a href="/blog/data-driven-theater/">Data-Driven Theater</a>. Consensus theater is its twin: same stage, different prop.</p>
<p><strong>The alignment ritual.</strong> &quot;Alignment meeting&quot; is frequently the phrase an organization uses when nobody is willing to say <em>&quot;I decide.&quot;</em> Alignment is a wonderful outcome and a terrible process goal: you align <em>around</em> a decision, not <em>instead of</em> one.</p>
<p>None of these people are acting in bad faith. That&#039;s the uncomfortable part. Each anti-pattern is locally rational: vetoes feel like diligence, consensus feels like respect, alignment feels like collaboration. The system produced by these good intentions cannot decide anything.</p>
<h2>ADR Records What, RAPID Records Who</h2>
<p>Architects already have a beloved decision artifact: the ADR, the Architecture Decision Record. I write them, I love them, I make agents read them. But an ADR documents <em>what</em> was decided and <em>why</em>. It says nothing about <em>who</em> had the right to decide it, and whether they actually did.</p>
<p>The two are complements, not competitors. RAPID (or any explicit decision contract; the brand doesn&#039;t matter) defines who; the ADR preserves what. Pair them and decisions become durable: made once, by a named person, with named input, written down.</p>
<p>Skip the first half and you get my favorite one-liner from this whole experience: <strong>an ADR without a decider is a wish with a timestamp.</strong></p>
<h2>One Question Instead of One More Meeting</h2>
<p>So here&#039;s my practical suggestion. Next time an architecture decision stalls, bouncing between forums and gathering workshops like a hobby, don&#039;t schedule another meeting. Ask one question, in writing, where everyone can see it:</p>
<p><strong>&quot;Who is the D on this?&quot;</strong></p>
<p>If the answer comes quickly, you&#039;ll have a decision within days. If the answer is silence… congratulations, you&#039;ve found the actual blocker, and it was never the technology.</p>
<p>What&#039;s the oldest undecided decision in your organization right now? And does it have a name attached, or just a meeting series?</p>
]]></content:encoded>
      <category>Decision Making</category>
      <category>Software Architecture</category>
      <category>Governance</category>
      <category>Corporate Culture</category>
      <category>Engineering Leadership</category>
    </item>
    <item>
      <title>Data-Driven Theater</title>
      <link>https://bfrackowiak.pl/blog/data-driven-theater/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/data-driven-theater/</guid>
      <pubDate>Mon, 01 Dec 2025 09:00:00 +0100</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>Everyone wants decisions backed by data. Almost nobody asks what the data went through to get there.</description>
      <content:encoded><![CDATA[<p>Before we start, a fair warning. What follows is a light reflection: a private opinion built entirely on private observations. There is zero scientific literature behind it. No studies, no citations, no methodology section. Just years of sitting in meeting rooms and paying attention. If that doesn&#039;t sound rigorous enough for you, good catch. That&#039;s exactly the kind of honesty about data sources I wish I saw more often.</p>
<p>I have lost count of how many meetings I&#039;ve attended that were officially <em>data-driven</em>. There was a deck. There were charts. At some point somebody said &quot;the data clearly shows.&quot; And yet, walking out, I couldn&#039;t shake the feeling that I had just watched a performance, not a decision.</p>
<blockquote><p>&quot;There are three kinds of lies: lies, damned lies, and statistics.&quot;</p></blockquote>
<p>Mark Twain popularized that line and attributed it to Benjamin Disraeli. Historians still can&#039;t confirm Disraeli ever said it. I love that detail: the most famous quote about misleading statistics ships with a broken attribution. Even the quote about data lying is a matter of interpretation.</p>
<h2>Same Words, Different Worlds</h2>
<p>Here is my core observation: <strong>making decisions based on data makes sense only when the organization speaks the same language, built on the same concepts.</strong></p>
<p>Take something as innocent as &quot;active user.&quot; For the product team it&#039;s anyone who logged in this month. For sales it&#039;s a paying account. For support it&#039;s someone who opened a ticket and is still breathing. Now put all three in one room, each armed with a chart about &quot;our active users,&quot; and enjoy the show. Everybody is right. Everybody is talking about a different universe.</p>
<p>As a solution architect, I spend a large part of my life on contracts between systems. We version APIs, we validate schemas, we break builds when a field changes its meaning. And then we walk into a meeting where two departments compare numbers built on definitions that were never aligned. These are <strong>semantic contracts between people that nobody ever wrote down</strong>. Being data-driven is empty if those contracts were never seriously considered. The dashboard is just the visible tip of an agreement that doesn&#039;t exist.</p>
<h2>The Preprocessing Trap</h2>
<blockquote><p>&quot;If you torture the data long enough, it will confess to anything.&quot;</p></blockquote>
<p>That one is usually pinned on economist Ronald Coase, also in a paraphrased form, because apparently we can&#039;t even quote people about data accurately.</p>
<p>Here&#039;s the uncomfortable part: data is surprisingly easy to manipulate, and you rarely need to lie in the data itself. The comfortable place to cheat is earlier, in the preprocessing phase. Which rows were dropped as &quot;invalid&quot;? Which period was chosen as &quot;representative&quot;? Which outliers were &quot;obviously&quot; errors? Every one of those choices is invisible in the final chart.</p>
<p>That&#039;s why I believe <strong>explaining how a dataset was cleaned and prepared is often exactly as important as the conclusions drawn from it</strong>. The conclusions are the last five percent of the journey. Everything that shaped the answer happened before.</p>
<p>And there are two symmetric ways to fail here:</p>
<ul>
<li><strong>Conclusions drawn from raw data</strong> give you a picture distorted by outliers. One huge client, one migration script, one QA account with ten thousand test orders, and your &quot;average&quot; is science fiction.</li>
<li><strong>Conclusions drawn from over-cleaned data</strong> give you optimization toward a thesis. Scrub away every inconvenient observation as an &quot;anomaly,&quot; and the data will happily confirm whatever you believed before you opened the file.</li>
</ul>
<p>Between those two extremes lives an honest analysis. You&#039;ll recognize it by one feature: it comes with a changelog.</p>
<h2>It&#039;s Rarely the Conclusions</h2>
<p>In organizations (I&#039;m deliberately not saying <em>corporations</em>, because this has nothing to do with company size) the problem I keep seeing is almost never the inference step. People are genuinely good at drawing conclusions. Give a smart group a dataset and they will reason about it just fine.</p>
<p>The problem sits earlier and deeper: <strong>the method of collecting the data, and the absence of a shared language to describe it.</strong> We argue passionately about what the numbers mean while quietly disagreeing, without knowing it, about what the numbers <em>are</em>. Calling that process data-driven doesn&#039;t fix it. It just gives the disagreement better slides.</p>
<h2>Unless…</h2>
<p>Of course, there is one scenario where none of this matters.</p>
<p>If your actual goal is not a decision but an appearance, walking into the meeting as <em>the prepared one</em> and radiating competence from behind a wall of charts, then yes, data helps enormously. A confident slide with big numbers is a wonderful prop.</p>
<p>But it works under exactly one condition: your audience must not be competent enough to ask about definitions and preprocessing. And trust me, that condition is satisfied more often than we&#039;d like to admit ;) If you&#039;re wondering how those audiences end up in those rooms, <a href="https://en.wikipedia.org/wiki/Peter_principle">The Peter Principle</a> has been explaining it since 1969.</p>
<h2>A Small Experiment</h2>
<p>So here&#039;s my suggestion for your next data-driven meeting. Don&#039;t challenge the conclusions. That&#039;s playing the game on the presenter&#039;s terms. Ask two questions instead:</p>
<ol>
<li><em>&quot;How exactly do we define this metric, and does everyone here use the same definition?&quot;</em></li>
<li><em>&quot;What was removed or transformed during cleaning, and why?&quot;</em></li>
</ol>
<p>Then watch the room.</p>
<p>If the answers come easily, congratulations: you&#039;re in one of those rare places where data-driven actually means something. If what you get is a pause, a nervous smile, and &quot;let&#039;s take it offline&quot;…</p>
<p>Well. Enjoy the theater. You&#039;re the audience.</p>
]]></content:encoded>
      <category>Data-Driven Decision Making</category>
      <category>Data Quality</category>
      <category>Organizational Culture</category>
      <category>Engineering Leadership</category>
      <category>Software Architecture</category>
    </item>
    <item>
      <title>Stop Calling It Legacy Code</title>
      <link>https://bfrackowiak.pl/blog/stop-calling-it-legacy-code/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/stop-calling-it-legacy-code/</guid>
      <pubDate>Mon, 19 Feb 2024 09:00:00 +0100</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>With AI in the picture, it&#039;s not legacy anymore. It&#039;s a Knowledge Container.</description>
      <content:encoded><![CDATA[<p>We&#039;ve all been there. You join a new team, clone the main repository, open your IDE, and immediately let out a heavy sigh. It&#039;s the dreaded <em>legacy codebase</em>. For years, &quot;legacy&quot; has been the ultimate dirty word in software engineering. It was the monster under the bed, the excuse for missed deadlines, and the justification for multi-million-dollar rewrite initiatives.</p>
<p>But I have a thesis that might make a few software architects uncomfortable: <strong>in the era of AI, legacy code no longer exists.</strong> The entire concept of &quot;legacy&quot; is expiring. What we are left with is something far more valuable, and it&#039;s time we change how we look at it.</p>
<h2>Why Did We Call It &quot;Legacy&quot; Anyway?</h2>
<p>Let&#039;s be honest with ourselves for a second. What actually makes code <strong>legacy</strong>?</p>
<p>Historically, code became legacy the exact moment developers simply didn&#039;t want to maintain it anymore. It was an incentive problem. The promise of working with brand-new, shiny technology means being more efficient, learning new skills, and therefore remaining highly competitive on the job market. Not everyone wants to put &quot;maintained a 10-year-old monolith&quot; at the top of their CV.</p>
<p>To justify moving away from it, we tended to argue that legacy code is inherently more costly to evolve and that adding new features is too slow.</p>
<p>But here is the reality check: <strong>code is incredibly cheap now.</strong> With AI, generating code is no longer the bottleneck. Legacy code isn&#039;t the primary factor slowing down system development; unnecessary <em>complexity</em> is what actually kills productivity.</p>
<p>Oh, and let&#039;s not forget the most important part: many times, that ugly, outdated, legacy code is exactly what is paying our monthly salaries.</p>
<h2>The Tech Stack Doesn&#039;t Matter Anymore</h2>
<p>The fact that the technology used in an older codebase isn&#039;t state of the art means absolutely nothing to a Large Language Model.</p>
<p>As long as the code passes your functional and non-functional acceptance criteria (like performance and scalability), the age of the syntax is irrelevant. What matters right now isn&#039;t the framework. It&#039;s the data stored <em>inside</em> the code.</p>
<h2>From Codebase to &quot;Knowledge Container&quot;</h2>
<p>This brings us to the core of the shift. If it&#039;s not legacy code anymore, what is it? It is a <strong>Knowledge Container</strong>.</p>
<p>Think about it. That repository is the single most accurate, battle-tested database your company owns regarding:</p>
<ul>
<li>Deeply embedded business processes.</li>
<li>Highly specific edge-case acceptance criteria.</li>
<li>Real-world workflows that no one currently working in the office actually remembers.</li>
</ul>
<p>In the past, code went out of date the moment a developer hit <em>commit</em>. Today, that commit just becomes highly structured training data, a rich knowledge base for AI agents to read, understand, and act upon.</p>
<h2>The Rise of the &quot;Dev Operators&quot;</h2>
<p>Because of this, we don&#039;t need massive, groundbreaking &quot;greenfield&quot; initiatives just to keep systems in sync with modern standards.</p>
<p>If the tech stack no longer matters, the role of the human changes. We are moving away from being traditional engineers who manually type out logic, to becoming <strong>&quot;Operators.&quot;</strong> An Operator&#039;s job isn&#039;t to write the code; their job is to leverage the Knowledge Containers, express the business intent to an AI agent, and accurately judge the final result.</p>
<p>The concept of legacy code simply won&#039;t work anymore in the long term. It&#039;s a relic of an era where humans had to read every line.</p>
<p>Now, I just wonder: if we stop tracking Technical Debt, how long will it take for us to invent a metric to measure a system&#039;s <strong>&quot;Knowledge Density&quot;</strong>?</p>
]]></content:encoded>
      <category>Legacy Systems</category>
      <category>Artificial Intelligence</category>
      <category>Software Architecture</category>
      <category>Knowledge Management</category>
      <category>Software Development</category>
    </item>
    <item>
      <title>The Automerger</title>
      <link>https://bfrackowiak.pl/blog/the-automerger/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/the-automerger/</guid>
      <pubDate>Thu, 09 Jul 2020 09:00:00 +0200</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>How we taught our repositories to merge themselves, and what two years of running it taught us.</description>
      <content:encoded><![CDATA[<p>If your team uses a branching model with more than two long-living branches, you know the ritual. A bug gets fixed on the release branch. Somebody has to remember to bring that fix back to develop, or the bug will happily reappear in the next release. Multiply that by several release branches, several teams, and several fixes per week, and you have a full-time job nobody applied for.</p>
<p>At the company I worked for, we lived exactly that. GitFlow-style branching, multiple active branches at once (master, develop, releases in flight), and changes that had to propagate across all of them. Manual coordination was error-prone and slow, and the errors were the expensive kind: a regression returning in production because one merge was forgotten.</p>
<p>So we automated it. We call it the <strong>Automerger</strong>.</p>
<h2>What It Does</h2>
<p>The Automerger is a script built with Cake, the C# build automation tool. Its job is deliberately boring:</p>
<ol>
<li>Detect which branches need to be synchronized.</li>
<li>Create a temporary branch and perform the merge there.</li>
<li>Validate the result by building the code and running the tests.</li>
<li>If everything is green, push the merge.</li>
<li>If there is a conflict or a failed build, stop and notify the developers responsible.</li>
</ol>
<p>The key design decision: <strong>the tool only involves humans when a human is actually needed.</strong> A clean merge that builds and passes tests does not deserve anyone&#039;s attention. A conflict does, and then the notification goes to the right team, not to everyone (alert fatigue is real).</p>
<p>Although the tool is written in C#, there is nothing .NET-specific about the workflow. It merges JavaScript and Java repositories just as happily; the validation step simply runs whatever build and test commands the repository defines. It plugs into our CI/CD platform (Azure DevOps in our case) and notifications go wherever the team lives, Slack included.</p>
<h2>Convention over Configuration</h2>
<p>The part I like most: the Automerger needs almost no configuration. It discovers branches and orders them by naming convention. If your branches follow a sensible pattern (and with GitFlow they already do), the tool can infer the merge direction on its own: fixes flow from the oldest release branch, through newer ones, into develop.</p>
<p>One honest requirement comes with that: the workflow relies on <em>real merges</em>, not cherry-picks. Cherry-picked commits look different to git, and the tool cannot reason about them. Convention over configuration works only if you keep the convention.</p>
<h2>Two Years Later</h2>
<p>After two years of running the Automerger every 3–4 hours during working days, the numbers settled around <strong>90% of merges completing fully autonomously</strong>: no conflicts, builds green, nobody interrupted. Nine out of ten of those &quot;remember to merge back&quot; tasks simply stopped existing.</p>
<p>The remaining 10% is exactly where humans belong: genuine conflicts, where two branches disagree about the same lines and somebody has to decide.</p>
<p>The lesson generalizes beyond git. Find the task that is repetitive, mechanical, and only occasionally requires judgment. Automate the mechanical majority, and route the judgment cases to people with full context attached. Developer time is too expensive to spend on merges that a script can prove safe.</p>
<p>What is the &quot;merge back to develop&quot; of your team, the recurring chore everyone does by hand because nobody sat down for a week to kill it?</p>
]]></content:encoded>
      <category>Developer Tools</category>
      <category>Automation</category>
      <category>Engineering Practices</category>
      <category>DevOps</category>
      <category>Software Engineering</category>
    </item>
    <item>
      <title>Technical Leader: Identity Disorder</title>
      <link>https://bfrackowiak.pl/blog/technical-leader-identity-disorder/</link>
      <guid isPermaLink="true">https://bfrackowiak.pl/blog/technical-leader-identity-disorder/</guid>
      <pubDate>Tue, 26 May 2020 09:00:00 +0200</pubDate>
      <dc:creator>Bartosz Frąckowiak</dc:creator>
      <description>Splitting one corporate title into its two words, and what each of them demands.</description>
      <content:encoded><![CDATA[<p>Hi, my name is Bartosz Frąckowiak, for over three years now I&#039;m the so-called Technical Leader and I would like to tell you my story :)</p>
<p>A technical leader can be just a simple position in the corporate hierarchy. It can be a safe position reserved for a person technically based with some leadership skills. Opposite to team leaders who generally should have strong leadership skills and a technical base, not necessarily be a guru in the programming stack. The question you should ask yourself is: what do you expect from your career?</p>
<p>One of the ingredients is your attitude. I have never wanted to make this title just a corporate position to me. The footer in corporate mail means nothing to me. After three years of being a technical leader, I&#039;ve learnt a lot about what it&#039;s like to be one of them (in this particular environment). I have ups and downs. One day I would never change this position to a different one, the next day I was almost ready to quit and move on. I would like to break down this position into smaller pieces for you. Let&#039;s start with splitting just the name &quot;technical leader&quot;. It has two words in it. The first part is strictly &quot;technical&quot;, the second is &quot;leader&quot;. I will try to address these two parts inside this article and explain what it means to me. What I do to be the best possible technical leader.</p>
<h2>&quot;Technical&quot;</h2>
<p>The technical part is all about widely known engineering craftsmanship. The whole category is extremely wide, so let&#039;s split it even more.</p>
<p><strong>Self-development.</strong> The first one is self-development in the context of engineering knowledge. So simple and yet so complex at the same moment. The technical leader needs to join the technical mastery journey no matter what he or she knows already. A technical leader is responsible for analyzing new approaches, new shiny bits and bobs that come to our engineering market. Follow trends, join conferences, and so on. As the market is going fast in various directions, it is a tough nut to crack.</p>
<p>Usually, inexperienced developers are highly motivated. Discovering the engineering world just makes them even happier to go further and further. Somewhere on the road, the developer finds out that it is almost impossible to be a specialist in every branch of engineering.</p>
<p>After some time you find it spiritually releasing to decide not to try to be a specialist in everything, because it is almost impossible. Just find your thing or two that drives you, makes you sleepless through the night, makes you search for the next piece of information about a particular topic. On other topics be up-to-date, more or less. Do you like data science? It&#039;s fine not to know the specification differences between OpenAPI 3.0 and 2.0. Just simply know it&#039;s there; you will deep dive into details when needed.</p>
<blockquote><p>&quot;Let him who would move the world first move himself.&quot; ― Socrates</p></blockquote>
<p><strong>Using knowledge.</strong> The second chapter is using engineering knowledge in the day-to-day workspace. Making the right decisions and leading the team to the chosen common goal, which should describe where the IT department wants to be in the long run. It&#039;s the technical leader&#039;s responsibility to make sure the goal itself correlates to the high-level shape of the software architecture. Architecture as simple as possible while meeting all the needs of the business. Easy to develop and easy to maintain in the long run.</p>
<p>Finding this architecture is a never-ending story. The company evolves and software should follow the development as well.</p>
<blockquote><p>&quot;Clean code is not written by following a set of rules. You don&#039;t become a software craftsman by learning a list of heuristics. Professionalism and craftsmanship come from values that drive disciplines.&quot; ― Robert C. Martin</p></blockquote>
<p><strong>Being a medium.</strong> The third category is being the technical medium and connector between the architect, other teams, and the team itself. At the moment we have eight teams with seven developers each. This is not a space where lack of proper communication can be accepted. Discussing with other technical leaders and the architect takes place twice per week.</p>
<p>Being a connector between the technical leaders&#039; community and the team is an important task which needs to be done every day. Making sure the team knows where we are heading. It&#039;s crucial to the whole IT environment to know what decision has been made and why the technical leaders&#039; community took this approach. In the long run, communication lets the team understand the goal where we are heading and, therefore, be more independent in making decisions and in the end more effective.</p>
<blockquote><p>&quot;The single biggest problem in communication is the illusion that it has taken place.&quot; ― George Bernard Shaw</p></blockquote>
<h2>&quot;Leader&quot;</h2>
<p>Each technical leader has a &quot;leader&quot; inside the position title. Leader means leadership for the team. How to be the best one for the team? Here I will present tangible ideas and practices that are introduced inside my team. I am sure that thanks to those, my team has improved a lot during these three years. Are those rules a golden bullet suited for every team in every corporation? For sure not… Simply looking from a statistical point of view, they will not fit every environment. I hope I can inspire you a bit and encourage discussion.</p>
<p><strong>Dev knowledge sharing.</strong> The first idea is about sharing knowledge in a safe space. I have booked one hour per week, usually at the end of the week when everyone is thinking about the weekend already. The idea is to create a safe space to share any topic you want.</p>
<p>Only one rule applies: you have to talk about engineering. There is no constraint that the topic has to be work-related or has to suit the project the team is working on. Simply, you have something to share? Come and talk about it. What&#039;s more, there are no rules about the presentation itself. If a developer wants to have a high-end presentation, it is appreciated; if a developer presents an idea from a scratch of paper, that&#039;s fine as well.</p>
<p>The idea found interest quickly and became a permanent part of the team&#039;s life. We had meetings where all of us were reviewing pull requests with some new approach, as well as meetings about machine learning algorithms. From TDD to ideas on how to optimize our team efficiency. From coding conventions to project managing strategies. Anything developers are interested in is suited for the local dev knowledge sharing.</p>
<p>After three years of hosting these meetings, I found out that the secret here is feeling safe inside the team. Each presenter received feedback during the meeting knowing that feedback is only for his good and self-improvement. There are no mean jokes, nobody criticizes that the topic isn&#039;t important in any context. It is simply time for a developer to shine and be proud of his knowledge.</p>
<p>These meetings evolved from local dev knowledge sharing to company-wide meetings. We keep both. Local team meetings stay the same, the safe place they were designed to be. There is also a place to share knowledge with the bigger crowd: the whole IT department. Sure, it requires a little more preparation than just a piece of paper and an idea. A nice presentation is one of the unwritten requirements. The audience is multinational, so it also requires globalization of your presentation. Globally hosted knowledge sharing meetings are a much bigger deal than the local ones. Both meetings are held hand in hand. In the end, the outcome is improved team knowledge with no additional cost.</p>
<blockquote><p>&quot;If you have knowledge, let others light their candles in it.&quot; ― Margaret Fuller</p></blockquote>
<p><strong>Meetups.</strong> Starting from the local dev knowledge sharing idea, we moved one step further. We hosted a meetup. The first one was about the approach to one of the projects we were involved in. We had space and time to learn (learn the hard way) TDD inside DDD.</p>
<p>We decided to share our journey with the local community. The event took place at our office, in a workshop formula, and it was specially designed to guide participants into the same mistakes we made. We showed how we handle challenges and what we had learned.</p>
<p><strong>Technical excellence meetings.</strong> On the other side, inside the team, we have team members, NOT resources. Each team member is a human being with a different background, different needs and different goals.</p>
<p>As a technical leader, there is enough space to show myself useful. The idea is to independently develop each team member&#039;s skills based on his or her needs. The concept name is &quot;technical excellence&quot;. There are two kinds of meetings during the month. The first meeting is quick, up to half an hour: a planning and negotiation session whose goal is to find the area that will be developed later. Ideas usually come from the developer; he or she knows best what they&#039;re interested in (well, sometimes it is not the case, as some people need a small hint). My role is to ensure the idea is valid at the basic level and to find common ground between developer needs and company goals.</p>
<p>With a plan in place, there is a proper longer meeting, usually around two hours once or twice per month. This relatively small time belongs completely to the developer. We can do whatever he or she wants as long as it brings educational value. During these three years we had meetings about blockchain, testing, many topics related to DDD, or specific Azure features. During the meeting we can research the topic itself, work on problems related to it, or even prepare a presentation that will be shown later at the IT department knowledge sharing meeting.</p>
<p>The ones that touched me the most are about self-improvement. The majority of my team members found themselves in a place where they want to take a &quot;step forward&quot;, the step that will distinguish them from every other backend developer. Those meetings are the most heart-touching, the hardest ones, but also the most inspiring and rewarding ones. I would recommend it to each technical leader: try to talk not only about facts and technology. Find a way to inspire team members, even a little bit.</p>
<p>The success of those meetings is based on properly planned time for the developer, way ahead. Each of us has some topic that he or she wants to handle when time allows. The truth is that there will be no free time. What&#039;s more, you will have less and less &quot;free time&quot; as your career evolves. Those meetings address the problem: the time frame cannot be moved, every participant needs to be prepared. The meeting must take place.</p>
<p>Every year there is a time of appraisals, the time when your performance gets reviewed. What is important to me, there are feedback forms that let others put opinions about me as a technical leader. In the past, I found a lot of mentions of technical excellence in the feedback forms, and most of them were very positive. For me, it is simply a win and the way to go.</p>
<p>Along with those meetings, I found two things that make me wonder a bit. Since each meeting was between me and another team member, I had to be prepared for every topic, which put pressure on my calendar. The idea I want to test is to follow the same meeting pattern, but with team members running the meetings for each other. I would just watch at the very beginning and help until the point where I&#039;m no longer needed. (At this moment I am part of a team of very experienced developers; the idea might not work with a less experienced team.)</p>
<p>The second matter that bothers me a bit is purely egoistic. Each team member gets a few hours per month to develop himself, but what about me? :) The answer is simple: I do technical excellence with myself (sounds like a split personality). I simply book some time during the month and learn what I&#039;m interested in the most. At the moment the hype is on deep learning.</p>
<blockquote><p>&quot;Be a yardstick of quality. Some people aren&#039;t used to an environment where excellence is expected.&quot; ― Steve Jobs</p></blockquote>
<p><strong>Pair programming.</strong> Again, simple as that and powerful as that. Pair programming in any form that suits the developer and the situation. Starting from just sitting together and being a rubber duck for the partner, to hardcore pure pair programming with the whole team inside a dedicated room. Having not just one additional pair of eyes but three members watching. A live coding session with a large screen presentation and discussion about specific problems.</p>
<p>Which method should we choose? The rule of thumb is to pick &quot;small&quot; pair programming (sitting together, switching keyboards from time to time) for smaller problems like bugs or planned tasks, and the &quot;whole team is watching&quot; meeting for solutions that will affect everyone&#039;s future work. Usually meetings about core decisions made at the beginning of a project, or changes in the direction the team was heading.</p>
<p>A lesson learned here for technical leaders: be prepared to monitor the involvement of others during &quot;everyone is watching&quot; meetings. The tool is so simple and so powerful; in terms of team building and knowledge sharing it is one of the best in my opinion. Be careful not to fall into the trap of being &quot;The Devil&#039;s Advocate&quot;, which causes frustration. Usually stepping back and taking a huge breath is the cure. After all, being pragmatic and empathic is crucial here.</p>
<p><strong>Open source initiative.</strong> Teams at our company are backend based. Usually we have a 5:2 ratio of backend to frontend developers inside a team. Team members produce source code daily. Most of the code is usually not visible to the end user and, therefore, it is harder to appreciate the craftsmanship of individuals.</p>
<p>Here, the idea was to share our concepts with the external world. Share it with the IT community in the form of YouTube videos, source code on GitHub, and articles on medium.com, exactly like this one. Each IT member can express himself or herself to the community about the topic he or she has mastered during nine-to-five work. Every piece of content is signed with the developer&#039;s name, no anonymous content. The sharing idea brings clear benefits to both sides. Developers build a brand for their names, and they build the company&#039;s brand as one with a strong engineering culture.</p>
<blockquote><p>&quot;Open source is a development methodology; free software is a social movement.&quot; ― Richard Stallman</p></blockquote>
<h2>Power of Small Changes</h2>
<p>All the tools and methods described here are working for my team. What I&#039;ve found during a few years of being a technical leader is that team evolution is faster than you think. Each member works hard to become a better version of himself or herself, not only during work hours but also after the nine-to-five period. To keep everyone interested there is a constant need for small improvements, small changes that keep the technical leader&#039;s toolset fresh and joyful for everyone.</p>
<p>If you find yourself in the same corporate position as mine, please share your experiences with the set of tools that you have developed over time.</p>
<blockquote><p>&quot;Great things are not done by impulse, but by a series of small things brought together.&quot; ― George Eliot</p></blockquote>
]]></content:encoded>
      <category>Technical Leadership</category>
      <category>Engineering Leadership</category>
      <category>Team Building</category>
      <category>Knowledge Sharing</category>
      <category>Corporate Culture</category>
    </item>
  </channel>
</rss>