michaelheap.comI write about systems, people, and what we learn when things go wrong.2026-04-29T09:01:50Zhttps://michaelheap.comMichael Heapm@michaelheap.comIt just has to work2026-04-29T09:01:50Zhttps://michaelheap.com/it-just-has-to-work/<p>We've been trained all of our work life to "do things the right way".</p>
<p>The right process. The right level of detail. The right amount of alignment.</p>
<p>You know what happens next. Six months later, nothing has shipped.</p>
<h2 id="the-hidden-cost-of-%22doing-it-right%22" tabindex="-1">The Hidden Cost of "Doing It Right"</h2>
<p>When you're a scrappy startup, everything is allowed. Quick hacks, half-baked ideas, and sometimes editing things live in production. But as a company grows, it starts to establish processes to prevent failure. A "best practice" here, or an "executive review" there.</p>
<p>Some of that is necessary. Every process is someone's scar. They've lived through outages, security incidents, compliance failures, and broken trust.</p>
<p>The problem is what happens over time. Last-resort safeguards quietly become prerequisites for action. Teams wait for a complete understanding of the problem. It rarely happens. When you treat perfection as a risk-avoidance strategy, nothing ever ships.</p>
<h2 id="momentum-beats-consensus" tabindex="-1">Momentum Beats Consensus</h2>
<p>One product manager I worked with hit this problem. They were struggling to get stakeholder time to talk about the requirements for a new capability. Everyone had opinions, and they wanted to make sure that they were captured.</p>
<p>I asked them to write a one-page, opinionated spec and share it. Within a day, stakeholders were commenting in the doc with their opinions.</p>
<p><strong>A one-page spec can trigger in a day what weeks of scheduling can’t: informed disagreement.</strong></p>
<p>The first version is almost always wrong, but it surfaces the <em>right</em> disagreements fast.</p>
<p>It doesn't need to be a spec. Sometimes it’s a clickable prototype. Sometimes it’s a command-line script, a mocked API, a Loom walkthrough. Just make <em>something</em> that people can react to.</p>
<p>It doesn’t need to be complete. "Working" means:</p>
<ul>
<li>Usable by a real person today</li>
<li>A measurable metric moves</li>
<li>You can tell that it worked within hours or days</li>
<li>It fits within your guardrails</li>
</ul>
<p>Incremental progress is how you get to the right answer faster. It requires clear success criteria and fast feedback loops. More importantly, it requires a willingness to delete work. Ship, learn, iterate, then ship again.</p>
<p>Some work has tight constraints - safety, legal, ethical, security - but the need to learn doesn't go away.</p>
<h2 id="the-real-questions" tabindex="-1">The Real Questions</h2>
<p>Action creates something people can react to. Once something "good enough" is unblocking real users, the questions change. Instead of asking "Is this ready?" or "Will this scale?", ask questions that inform the future.</p>
<ul>
<li>“What will we learn if this works?”</li>
<li>“How quickly will we know if it doesn’t?”</li>
<li>“What gets better the moment this exists?”</li>
</ul>
<p>Most teams fail not because they move too fast, but because they wait for certainty that never arrives. Teams that wait for perfect don't have better outcomes. They just have fewer of them. Progress starts when there’s something real to push against.</p>
<p>Treating every decision as permanent is how you end up six months in with nothing shipped.</p>
Disable VSCode embedded browser2026-04-27T20:05:28Zhttps://michaelheap.com/vscode-embedded-browser/<p>VSCode recently started opening my blog previews as an editor tab rather than in my normal browser. It turns out that this is a new <em>Integrated Browser</em> feature inside VSCode Insiders.</p>
<p>To disable it, add the following to <code>settings.json</code>:</p>
<pre class="shiki css-variables" style="background-color: var(--shiki-color-background); color: var(--shiki-color-text)"><div class="language-id">json</div><div class="code-container"><code><div class="line"><span style="color: var(--shiki-token-string-expression)">"workbench.browser.openLocalhostLinks"</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-constant)">false</span></div></code></div></pre>
<p>Or you can search for <code>workbench.browser.openLocalhostLinks</code> in the settings panel and uncheck the box.</p>
<p>Then restart VSCode, or reload the window with <em>Command Palette → Developer: Reload Window</em>.</p>
When AI beats procrastination2026-04-20T13:09:18Zhttps://michaelheap.com/when-ai-beats-procrastination/<p>I have a list. I’m sure you have a list too. A list of all the things that could be, if only you had more time. Time to explore the idea. Time to build a prototype. Time to invest in something new.</p>
<p>And if you’re anything like me, time is an excuse. Once I get started on a project, it becomes all-consuming. I find the time - 10 minutes here, 10 minutes there.</p>
<p>If I'm being honest, the list isn’t full of hard problems. It’s full of things that never felt important enough to start.</p>
<h2 id="human-vs-ai" tabindex="-1">Human vs AI</h2>
<p>A year ago, I thought AI was pretty terrible. It was too slow. Too unpredictable. Too willing to confidently invent nonsense.</p>
<p>I’d look at its output, think “I could do this better,” then walk off, adding the idea to the growing pile of things I was technically capable of doing but never motivated enough to begin.</p>
<p>The mistake I was making was simple: I was comparing AI to a human.</p>
<p>I was asking, “Is this as good as if I did it myself?” and the answer was usually “no.”</p>
<p>But I was asking the wrong question. What I should have been asking was “would I ever do this at all?”</p>
<h2 id="the-breakthrough" tabindex="-1">The breakthrough</h2>
<p>I’ve been meaning to build a CLI that wraps <a href="http://listentotaxman.com/">listentotaxman.com</a> so that I can compare two salaries for <em>years</em>. With a full-time job, a family, and other interests, it never quite made it to the top of the list.</p>
<p>So one Saturday evening, I took 10 minutes to <a href="https://michaelheap.com/listentotaxman-cli/">write down what I wanted</a>:</p>
<ul>
<li>the commands</li>
<li>what each should do</li>
<li>a couple of example outputs</li>
</ul>
<p>Then I walked away and made dinner.</p>
<p>When I came back, I had something that <em>mostly</em> worked. It wasn’t perfect. It’s definitely not something I’d ship to a customer. But for infrequent, personal use? It was <em>perfect</em>. Far better than the “this would be nice to build someday” alternative.</p>
<p>This was the breakthrough for me. <strong>It’s not “AI vs. Michael.” It’s “AI doing the work I never make time for.”</strong></p>
<h2 id="getting-started-is-the-hard-part" tabindex="-1">Getting started is the hard part</h2>
<p>Realising this changed how I approach my backlog.</p>
<ul>
<li>I try more things, because failure is cheap.</li>
<li>I finish more things, because “good enough” is easy to reach.</li>
<li>I revisit old ideas, because they’re no longer competing with everything else for my time.</li>
</ul>
<p>I used to have more ideas than time. I had to choose between spending an evening at my desk or with my family. That's no longer the case.</p>
<p>In two months, I built six tools I’d been putting off for years:</p>
<ul>
<li><a href="https://michaelheap.com/listentotaxman-cli/">listentotaxman-cli</a> - Compare multiple salaries using <a href="http://listentotaxman.com/">listentotaxman.com</a></li>
<li><a href="https://michaelheap.com/gh-saved-issues/">gh-saved-issues</a> - Configure <a href="http://github.com/issues">github.com/issues</a> from a config file</li>
<li><a href="https://github.com/mheap/agent-en-place">agent-en-place</a> - Sandbox agents using dynamically built environments with <code>mise</code> and <code>docker</code></li>
<li><a href="https://github.com/mheap/google-contacts-backup">google-contacts-backup</a> - Back up and restore your Google contacts to JSON</li>
<li><a href="https://michaelheap.com/actions-update-aur/">Automatic AUR updates with GitHub Actions</a></li>
<li><a href="https://github.com/mheap/trace2mermaid">trace2mermaid</a> - Render OpenTelemetry traces using <code>mermaid</code></li>
</ul>
<p>None of these are groundbreaking. And honestly, that’s the point. I couldn’t justify the time investment. But now they exist where they didn’t before.</p>
<h2 id="ai-is-still-untrustworthy" tabindex="-1">AI is still untrustworthy</h2>
<p>Let’s be clear: I don’t trust any of the code. It's brittle. I’m certain there are bugs I haven’t found (mostly because I haven’t looked very hard). AI has made assumptions that I haven’t validated.</p>
<p>AI is not how you build things you care deeply about.</p>
<p>It’s how you build things you want to exist.</p>
<p>AI doesn’t replace deep thinking and craft. It’s a shortcut around the part where nothing happens at all.</p>
Prompt Engineering for Humans2026-03-31T20:14:17Zhttps://michaelheap.com/prompt-engineering-for-humans/<p>A while back, I was working on <a href="https://github.com/mheap/trello-cli">trello-cli</a>, a CLI tool I maintain. I've been slowly adding commands as time allows, but this time I wanted to try out this new-fangled "agentic development" thing that everyone is talking about.</p>
<p>So I opened up my terminal, asked it to "Add support for showing card details" and let it churn away. Once it was done I reviewed the output and, honestly, I was disappointed. It added a command that showed the board title, description, and labels. It was missing the card's due date, which is the most important information of all. Checklist support was nowhere to be seen.</p>
<p>I went to close my laptop, safe in the knowledge that AI wasn't coming for my job any time soon. Then I realised, the agent had done what I asked. It had added support for showing card details. I didn't specify <em>which</em> details I wanted to see.</p>
<p>So I gave it another go, this time assuming it didn't know anything about my intent:</p>
<blockquote>
<p>Add a card:show command that accepts --board and --list as flags. Output the card title, description, due date, any attached checklists and labels. If any of these values do not exist, skip the header for that section. If checklists have more than 10 items, add a "+ X more" entry. Add an --all-details flag to show all of the information.</p>
</blockquote>
<p>This time the output was fantastic. It was a command I could use immediately. What would have easily taken me an hour was finished in minutes. Nothing about the model changed. The only thing that changed was the context.</p>
<p>The fastest way to get bad output from an AI system is to give it a vague prompt. It turns out the same is true for humans, too.</p>
<h2 id="management-%3D-context" tabindex="-1">Management = Context</h2>
<p>This is exactly what good managers do. Or at least what they’re supposed to do. Good managers, it turns out, spend a lot of time doing <strong>prompt engineering for humans</strong>.</p>
<p>Vague prompts produce vague results. From AI. From teams. From organisations.</p>
<p>Management isn't about assigning work. It’s about designing <em>context</em>: goals, constraints, expectations, and validation. When those things are clear, teams move quickly and independently. When they aren’t, people guess.</p>
<h2 id="the-one-sentence-management-problem" tabindex="-1">The one-sentence management problem</h2>
<p>Managers often give direction the same way people write bad AI prompts: short, clear, and wildly underspecified.</p>
<p>Here's something that I said in a documentation project at work:</p>
<blockquote>
<p>“All the information needed to achieve a task must be on a single page.”</p>
</blockquote>
<p>To me this was a precise requirement. Users must be able to achieve their goal by reading the content on a single page. What I didn't realise is that I had a set of assumptions about what that meant.</p>
<p>Bad prompts to AI fail fast; bad prompts to humans fail expensively.</p>
<p>Reverse-engineering intent from an underspecified prompt takes a <em>lot</em> of time, and it's usually missing nuance.</p>
<h2 id="the-rewrite" tabindex="-1">The rewrite</h2>
<p>In this case the team built something that met the requirements, but didn't match my expectations. I could figure out how to achieve a use case from the page, but it was a painful experience copying arcane configs into various files. If I didn't read closely I'd paste the wrong thing into the wrong file, and none of it would work.</p>
<p>After some deliberation, I rephrased the requirements:</p>
<blockquote>
<p>Users must be able to copy and paste down a page from top to bottom and be successful with their task. They must also have a way to validate that it's working.</p>
</blockquote>
<p>The goal didn’t change. The <strong>context</strong> did.</p>
<p>Now we understand how the user behaves. We know what success looks like. We know how the user verifies the outcome.</p>
<h2 id="why-constraints-create-autonomy" tabindex="-1">Why constraints create autonomy</h2>
<p>Managers sometimes hesitate to define constraints. They worry that too many rules will limit creativity. In practice, the opposite happens. Constraints remove ambiguity.</p>
<p>When teams understand the boundaries - what matters, what doesn’t, and how success will be measured - they stop waiting for clarification and start making decisions.</p>
<p>When projects struggle, the instinct is often to blame execution. The team misunderstood the task. The engineer built the wrong thing. The feature didn’t meet expectations.</p>
<p>Sometimes that’s true. But more often the real problem is simpler: the context wasn’t clear.</p>
<h2 id="the-context-checklist" tabindex="-1">The context checklist</h2>
<p>Before assigning work, ask yourself whether the prompt you’re giving your team includes five things:</p>
<p><strong>The goal</strong><br />
What outcome are we trying to achieve?</p>
<p><strong>The context</strong><br />
Why does this matter?</p>
<p><strong>The constraints</strong><br />
What boundaries exist around time, scope, or resources?</p>
<p><strong>The success criteria</strong><br />
What does good look like?</p>
<p><strong>The validation mechanism</strong><br />
How will we know it works?</p>
<p>When these pieces are clear, the team doesn’t need constant clarification or supervision.</p>
<h2 id="specify%2C-don't-guess" tabindex="-1">Specify, don't guess</h2>
<p>AI didn’t introduce a new problem. It exposed an old one.</p>
<p>We’ve always relied on people to interpret vague instructions, fill in gaps, and guess at intent. Sometimes that works when you're a small team. At scale, it breaks.</p>
<p>Prompt engineering is just a new name for something managers have always been responsible for: making sure the work is understandable before it begins.</p>
<p>The difference now is that there’s no illusion. Machines don’t guess what you meant. They reflect exactly what you said and you have to deal with the consequences.</p>
In the spotlight2026-02-26T14:58:41Zhttps://michaelheap.com/in-the-spotlight/<p>I've spent most of my career on the periphery. Building the tools, documentation and platforms that make it possible to build the product that customers pay for. I remember fighting tooth and nail for headcount. Lobbying to get some time on the company all-hands to make sure people know that we exist. Being worried that when cuts inevitably come, the teams I work with would be first on the chopping block.</p>
<p>Now, I'm in the spotlight. I'm the product leader for the product that makes the majority of our revenue. I fought so long to be in the spotlight, and now that I'm here I'm struggling to bear its weight.</p>
<h2 id="the-weight-of-the-spotlight" tabindex="-1">The weight of the spotlight</h2>
<p>Working on a revenue-generating product is often treated as a privilege. The work is visible. Impact is easier to point to. Success maps cleanly to business outcomes.</p>
<p>But that visibility comes with scrutiny.</p>
<p>Every decision is observed, evaluated, and challenged - by leadership, sales, marketing, finance, customer success, and often customers themselves. The closer your work is to revenue, the more people feel entitled to an opinion about it.</p>
<p>I thought I'd spend my time adding new capabilities to the product, but I've learned that in the spotlight you don't ship features. You defend them.</p>
<h2 id="revenue-%3D-restrictions" tabindex="-1">Revenue = Restrictions</h2>
<p>The closer your work is to the revenue, the less freedom you have on your own destiny. With so many people's careers relying on your product (internally, and within your customers) you're subject to a large list of constraints:</p>
<ul>
<li>Features promised to sign a deal</li>
<li>Existing customer expectations</li>
<li>Revenue targets from the business</li>
</ul>
<p>As you try to deliver all of these, you also need to maintain stability for your existing customers. You can't break anything, ever. Experimentation becomes expensive. Reversibility matters more than elegance.</p>
<p>You're walking down a narrow corridor under a bright light, stepping carefully do you don't touch the wall on either side. As you walk, people are shouting their opinions on pricing, roadmap priorities and market positioning.</p>
<p>And you can't ignore those opinions. Each statement is framed as risk. If we don't ship feature X, customer Y will churn. If we charge $10,000, we'll lose the deal. We need to shift messaging to CIOs to capture that budget.</p>
<h2 id="system-breakdown" tabindex="-1">System breakdown</h2>
<p>Over time, everything grinds to a halt.</p>
<ul>
<li>People ship the safe thing, not the impactful thing</li>
<li>Timelines are extended so that we can "underpromise and overdeliver"</li>
<li>People optimize for defensibility rather than learning</li>
</ul>
<p>This isn't your team doing the wrong things. They're reacting to the risk in the system with a rational response. No-one wants to be associated with a loss of revenue due to a bet they made. So we keep doing the safe things.</p>
<h2 id="the-trade-off" tabindex="-1">The trade-off</h2>
<p>When I think back to my time on auxiliary teams, there was no spotlight. We'd occasionally get feedback from a customer, but overall our destiny was our own. We could make the big bets, but when they were successful no-one really understood just how impactful the work was.</p>
<p>For auxiliary teams, success looks like nothing breaking, and no-one complaining. They keep the lights on all while longing for the spotlight themselves, not realising that there's a cost to pay.</p>
<p>Revenue teams trade autonomy for visibility.<br />
Auxiliary teams trade visibility for autonomy.</p>
<p>Neither is easier. They just fail in different directions.</p>
<h2 id="learning-to-carry-the-light" tabindex="-1">Learning to carry the light</h2>
<p>I don't think the answer is to work in the shadows. Revenue work is what pays the bills, and the scrutiny exists for real reasons. It's rational for people to protect it.</p>
<p>But the cost of that protection is often invisible: slower learning, narrower ambition, and teams that mistake <em>defensibility</em> for <em>good judgment</em>. When everything is framed as existential risk, we stop asking what’s worth building and start asking what’s hardest to argue against.</p>
<p>The work, I’m learning, isn’t just product strategy. It’s systems design. Creating space where teams can make bets without being crushed by hindsight. Separating real risk from imagined risk. Absorbing pressure so it doesn’t flatten everyone else.</p>
<p>That's the role of a leader. To make space. To bring clarity. To be the filter that brings signal to the noise.</p>
<p>The spotlight isn’t going away. But maybe it doesn’t have to blind us.</p>
Choose your losses2026-02-18T17:13:44Zhttps://michaelheap.com/choose-your-losses/<p>Strategy is choosing what you’ll be bad at.</p>
<p>I’ve been revisiting Michael Porter’s work on strategy, and the point that's stuck with me is this: trying to be “the best” is usually a trap. “Best” often means you’re competing on the same dimensions as everyone else. The same benchmarks, same dashboards, same playbook. You can lead for a while, but the moment everyone copies what works, yesterday’s advantage becomes today’s expectation.</p>
<p>If you want to avoid the race to the bottom, don't try and compete on doing the same things as everyone else, but better. Do the things that you're uniquely positioned to do that are hard to copy for your competitors.</p>
<h2 id="stop-confusing-%E2%80%9Cbetter%E2%80%9D-with-%E2%80%9Cdifferent%E2%80%9D" tabindex="-1">Stop Confusing “Better” With “Different”</h2>
<p>Companies often confuse differentiation with excellence. Maybe you have more features, or higher quality, or better customer support. Those <em>are</em> differentiators, but guess what:</p>
<ul>
<li>Your competitors are building those features right now</li>
<li>You're only one bad bug away from reputational damage</li>
<li>Your best support staff will leave (potentially for your competitors)</li>
</ul>
<p>The things that we say make us different are mostly <em>operational effectiveness</em> rather than <em>strategic differentiation</em>.</p>
<p>Strategic differentiation is the thing that's hard to copy. It's a set of choices that reinforce each other to build an advantage. Copying a single choice that you've made doesn't help the copier. They'd have to copy the whole system (and accept the trade-offs that you've made along the way).</p>
<p>This is how you differentiate yourself from the market.</p>
<p>The uncomfortable part is you have to name what you’re <em>not</em> going to optimize.</p>
<p>If you can’t say what you choose to be bad at, you don’t have a strategy, you have ambition.</p>
<h2 id="choose-your-customer" tabindex="-1">Choose Your Customer</h2>
<p>Let's build a sample use case to really drive the point home. There has been a proliferation of observability tools over the last couple of years. Thanks to open standards (thanks, OpenTelemetry!) it feels like a new competitor has popped up every other week and the cost of switching is lower than it's ever been. Competing on better pricing or a nicer UI is a dead end.</p>
<p>Many of the tools out there position real-time data processing as a benefit. The event happens, and less than 5 seconds later you can see it in your dashboard. Your system can do it in 3 seconds, so you're the market leader for those looking for speed.</p>
<p>Eventually though, someone else will do it in 2.5 seconds. Then 2, then 1. Suddenly your advantage is gone.</p>
<p>Instead of competing on the same metric that everyone uses, what if you explored other use cases:</p>
<ul>
<li>These tools have 30-day retention -> We'll provide 365-day retention at the same granularity (targeting compliance) -> Storage and retrieval of the data takes up to 10 minutes</li>
<li>Sending so much data across the internet is expensive -> Our product can't show realtime, fine-grained data, but we have an agent that runs daily and reduces data egress costs by 90% (targeting FinOps teams) -> Additional deployment complexity for customers</li>
<li>We pride ourselves on performance -> We're slightly slower to ingest, but we guarantee exactly once ingestion (targeting regulated industries) -> Our sales team finds it hard to demo</li>
</ul>
<p>Each of these use cases forces architecture, pricing and go-to market decisions. They are hard to imitate, and hard to undo. If sales asks for realtime dashboards, the strategy answer isn’t ‘maybe later.’ It’s ‘no’ unless we’re willing to unwind the system that makes our advantage.</p>
<p>If you're successful, your competitors will try to copy you without having the correct foundations. They built an architecture around ingestion speed and are trying to pivot to long-term retention with the wrong technologies, and the wrong team.</p>
<p>It's important to emphasize that these are <strong>trade-offs</strong>. If the market values speed over retention, you don’t get to “pivot” to instant ingestion without undoing the choices that made you different. You can't do everything <em>and</em> be differentiated.</p>
<h2 id="build-a-stack%2C-not-a-skill" tabindex="-1">Build a Stack, Not a Skill</h2>
<p>Most career advice is generic. "Be a great communicator". "Always deliver on time". When I'm reviewing applications as a hiring manager, I've seen all of the generic traits by the fifth or sixth application.</p>
<p>Instead of saying the same as everyone else, build a specific combination of traits that sets you apart.</p>
<p>To build your stack of traits, choose one item from each of these categories:</p>
<ol>
<li>
<p>Domain / context (where you have unusual understanding)<br />
Examples: platforms, regulated industries, developer workflows, migrations, pricing, enterprise buying, support realities.</p>
</li>
<li>
<p>Craft (how you work)<br />
Examples: writing, systems thinking, facilitation, debugging, prototyping, negotiation, research.</p>
</li>
<li>
<p>Leverage (how it spreads)<br />
Examples: internal enablement, templates, docs, workshops, tooling, a repeatable process, a network.</p>
</li>
</ol>
<p>Your uniqueness usually lives in the combination, not a single skill.</p>
<p>Example: “Deep platform context + crisp writing + enablement templates”<br />
becomes: someone who makes complex platform decisions simple and repeatable across teams.</p>
<p>As mentioned earlier, there are trade-offs each time you make a decision. Name the trade-off you're willing to make consistently:</p>
<ul>
<li>I'll lose five small deals to save one large deal</li>
<li>I care more about getting to what’s true than about everyone agreeing</li>
<li>I prioritise iteration speed over upfront certainty</li>
<li>We optimise for consistency, not autonomy</li>
<li>I aim to understand 25% of Product, Engineering and Marketing rather than 80% of Product</li>
</ul>
<p>Personally, I am the person you call when something is on fire, because I can rapidly gain context from both a technical and business perspective. To enable this, I will happily be mediocre at long-horizon planning and stakeholder consensus-building.</p>
<h2 id="the-pattern-shows-up-everywhere" tabindex="-1">The Pattern Shows Up Everywhere</h2>
<p>Being unique is a strategy that works in every industry.</p>
<p><strong>IKEA</strong> optimise for low prices and immediate availability, at the expense of service through flat-pack, self-assembly, and “good enough” furniture.</p>
<p><strong>FedEx</strong> optimise for reliable, on-time delivery rather than aiming to be the cheapest option.</p>
<p><strong>Nespresso</strong> optimise for convenient premium-at-home coffee, at the expense of a closed pod ecosystem and a higher cost per cup.</p>
<p>That's how you build an advantage, not by competing in a race where the prize is eventually becoming everyone’s baseline. Strategy is choosing what you’ll refuse to be great at - and then living with that refusal long enough that it becomes your advantage.</p>
Never leave on a no2026-02-13T03:52:05Zhttps://michaelheap.com/never-leave-on-a-no/<p>One of the biggest mistakes I see new managers make is how they manage disagreement with their boss.</p>
<p>They do one of two things:</p>
<ul>
<li>They nod along, but disagree quietly and disengage</li>
<li>They keep arguing until they run out of time and the meeting ends awkwardly, with no real decision</li>
</ul>
<p>Both options leave their boss frustrated.</p>
<p>Something that's worked well for me is that I <strong>never leave a conversation on a no</strong>.</p>
<p>It doesn't mean we always agree. It means that we're aligned on what the next steps are.</p>
<h2 id="disagreeing-behind-closed-doors" tabindex="-1">Disagreeing behind closed doors</h2>
<p>I believe that it's everyone's responsibility to challenge bad ideas. You should challenge ideas when:</p>
<ul>
<li>You have information that the person you're talking to doesn't</li>
<li>You're accountable for the outcome</li>
<li>You believe a decision will materially harm your team, customers or business</li>
</ul>
<p>How you challenge is important. Shouting in a meeting room with 15 other people won't get you anywhere. Instead, argue with your boss <em>in private</em> where there is space to listen and to be heard.</p>
<h2 id="know-when-to-stop" tabindex="-1">Know when to stop</h2>
<p>There's a difference between advocacy and infinite debate. Knowing when to stop pushing and start aligning is a skill.</p>
<p>Early in a discussion you can push hard:</p>
<ul>
<li>Ask clarifying questions</li>
<li>Share concrete risks</li>
<li>Offer alternatives, not just objections</li>
</ul>
<p>But at some point, you'll feel a shift in the conversation. You start getting repeated answers. The trade-offs have been heard. The decision has been made.</p>
<p>The most senior person in the room decides when the argument is over. Your job is to notice.</p>
<p>Missing that moment once is forgivable. Missing it repeatedly gets you labeled “difficult,” regardless of the quality of your ideas.</p>
<p>Disagreement is a tool for improving decisions, and once the decision is made your job changes.</p>
<h2 id="what-%E2%80%9Cnever-leave-on-a-no%E2%80%9D-actually-means" tabindex="-1">What “never leave on a no” actually means</h2>
<p>Before the meeting ends, alignment has to be explicit.</p>
<p>You should be able to say one of these - and mean it:</p>
<ul>
<li>“I disagree, but I understand the decision and I’ll execute it.”</li>
<li>“I still have concerns, but given the trade-offs, this is the direction we’re taking.”</li>
<li>“Let’s try this for X weeks and revisit with data.”</li>
</ul>
<p>A clean disagreement that ends in commitment builds trust. A fuzzy disagreement that lingers destroys it.</p>
<p>From your boss’s perspective, there are few things more dangerous than a manager who:</p>
<ul>
<li>Appears aligned in the meeting</li>
<li>Signals doubt afterward</li>
<li>Executes half-heartedly</li>
</ul>
<p>If you can’t commit, say so <em>in the room</em>. Don’t carry a private veto.</p>
<h2 id="sharing-with-your-team" tabindex="-1">Sharing with your team</h2>
<p>It's easy to paint leadership as the bad guys.</p>
<ul>
<li>"This wasn't my call"</li>
<li>"I don't agree, but..."</li>
<li>"Leadership wants us to..."</li>
</ul>
<p>It feels like solidarity, but it teaches your team that alignment is optional.</p>
<p>Your team works for <em>you</em>, not leadership, and they need to know what they need to do be successful in your business.</p>
<ul>
<li>What was the decision?</li>
<li>Why did we decide this?</li>
<li>What does success look like?</li>
</ul>
<p>You don't have to say that you're fully behind the idea, but you <em>do</em> need to communicate that this is the direction.</p>
<blockquote>
<p>“There were other options, and we discussed them. This is the direction we’re taking, and here’s how we’ll make it successful.”</p>
</blockquote>
<p>If you can't stand behind the decision at all, you need to get back in a room with your boss and talk until you can.</p>
<h2 id="your-real-job" tabindex="-1">Your real job</h2>
<p>Your job isn’t to win arguments.<br />
It’s to reduce uncertainty.</p>
<p>Argue fiercely when it matters.<br />
Notice when the argument is over.<br />
And never walk out of the room without a clear answer to one question:</p>
<p><strong>“What are we doing next?”</strong></p>
Update the AUR from GitHub Actions2026-02-05T13:45:31Zhttps://michaelheap.com/actions-update-aur/<p>I maintain <a href="https://github.com/mheap/trello-cli">trello-cli</a>, which is also published to the Arch User Repository (AUR).</p>
<p>Each time the package is published, I needed to update the <code>PKGBUILD</code> with a new version and <code>sha512sum</code>. I'm no longer an Arch user (I miss it, but MacOS works so much better for my current role) and so I don't have <code>makepkg</code> readily available.</p>
<p>I had an <a href="https://michaelheap.com/ideas">idea</a> that updating the <code>PKGBUILD</code> could be done with GitHub Actions:</p>
<blockquote>
<p>Updating an AUR package for a node based tool could be automated using GitHub Actions.<br />
Each time a release is tagged, the <code>pkgver</code> and <code>sha512sums</code> fields in the <code>PKGBUILD</code> need updating, and the repo needs pushing to the <a href="https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD">https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD</a><br />
The <code>sha512sum</code> needs to be calculated from the published NPM package, so we'd need to wait until the version is available, download it from the registry and then calculate the checksum.</p>
</blockquote>
<p>Here's how I did it:</p>
<ol>
<li>
<p>Generate an SSH keypair:</p>
<pre class="shiki css-variables" style="background-color: var(--shiki-color-background); color: var(--shiki-color-text)"><div class="language-id">bash</div><div class="code-container"><code><div class="line"><span style="color: var(--shiki-color-text)">ssh-keygen -t ed25519 -f </span><span style="color: var(--shiki-token-keyword)">~</span><span style="color: var(--shiki-color-text)">/.ssh/aur -C </span><span style="color: var(--shiki-token-string-expression)">"GH AUR - your-email@example.com"</span></div></code></div></pre>
</li>
<li>
<p>Add the public key to your AUR account:</p>
<ul>
<li>Go to <a href="https://aur.archlinux.org/">https://aur.archlinux.org</a> → My Account → SSH Public Keys</li>
<li>Paste contents of <code>~/.ssh/aur.pub</code></li>
</ul>
</li>
<li>
<p>Add the private key as a GitHub secret:</p>
<ul>
<li>Go to your repo → Settings → Secrets and variables → Actions</li>
<li>Create secret named <code>AUR_SSH_PRIVATE_KEY</code></li>
<li>Paste contents of <code>~/.ssh/aur</code></li>
</ul>
</li>
</ol>
<p>Then create a GitHub Actions workflow that you can run. You can't add it to your <code>npm release</code> workflow as there is a delay between publishing and it appearing in the API.</p>
<p>There's some tricky <code>runuser</code> logic required when updating <code>.SRCINFO</code> as you can't run <code>makepkg</code> as <code>root</code>:</p>
<pre class="shiki css-variables" style="background-color: var(--shiki-color-background); color: var(--shiki-color-text)"><div class="language-id">yaml</div><div class="code-container"><code><div class="line"><span style="color: var(--shiki-token-keyword)">name</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">Update AUR Package</span></div><div class="line"></div><div class="line"><span style="color: var(--shiki-token-constant)">on</span><span style="color: var(--shiki-token-keyword)">:</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">workflow_dispatch</span><span style="color: var(--shiki-token-keyword)">:</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">schedule</span><span style="color: var(--shiki-token-keyword)">:</span></div><div class="line"><span style="color: var(--shiki-color-text)"> - </span><span style="color: var(--shiki-token-keyword)">cron</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"0 0 * * 0"</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-comment)"># Weekly on Sunday at midnight UTC</span></div><div class="line"></div><div class="line"><span style="color: var(--shiki-token-keyword)">jobs</span><span style="color: var(--shiki-token-keyword)">:</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">update-aur</span><span style="color: var(--shiki-token-keyword)">:</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">runs-on</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">ubuntu-latest</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">container</span><span style="color: var(--shiki-token-keyword)">:</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">image</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">archlinux:base-devel</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">steps</span><span style="color: var(--shiki-token-keyword)">:</span></div><div class="line"><span style="color: var(--shiki-color-text)"> - </span><span style="color: var(--shiki-token-keyword)">name</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">Install dependencies</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">run</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">pacman -Sy --noconfirm jq openssh git</span></div><div class="line"></div><div class="line"><span style="color: var(--shiki-color-text)"> - </span><span style="color: var(--shiki-token-keyword)">name</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">Get latest npm version</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">id</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">npm</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">run</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">|</span></div><div class="line"><span style="color: var(--shiki-token-string)"> VERSION=$(curl -s https://registry.npmjs.org/trello-cli/latest | jq -r '.version')</span></div><div class="line"><span style="color: var(--shiki-token-string)"> echo "version=$VERSION" >> $GITHUB_OUTPUT</span></div><div class="line"></div><div class="line"><span style="color: var(--shiki-color-text)"> - </span><span style="color: var(--shiki-token-keyword)">name</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">Download tarball and get sha512sum</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">id</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">checksum</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">run</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">|</span></div><div class="line"><span style="color: var(--shiki-token-string)"> curl -sLO "https://registry.npmjs.org/trello-cli/-/trello-cli-$.tgz"</span></div><div class="line"><span style="color: var(--shiki-token-string)"> SHA512=$(sha512sum trello-cli-$.tgz | cut -d' ' -f1)</span></div><div class="line"><span style="color: var(--shiki-token-string)"> echo "sha512=$SHA512" >> $GITHUB_OUTPUT</span></div><div class="line"></div><div class="line"><span style="color: var(--shiki-color-text)"> - </span><span style="color: var(--shiki-token-keyword)">name</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">Setup SSH for AUR</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">run</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">|</span></div><div class="line"><span style="color: var(--shiki-token-string)"> mkdir -p ~/.ssh</span></div><div class="line"><span style="color: var(--shiki-token-string)"> echo "$" > ~/.ssh/aur</span></div><div class="line"><span style="color: var(--shiki-token-string)"> chmod 600 ~/.ssh/aur</span></div><div class="line"></div><div class="line"><span style="color: var(--shiki-color-text)"> - </span><span style="color: var(--shiki-token-keyword)">name</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">Clone AUR repo</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">env</span><span style="color: var(--shiki-token-keyword)">:</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">GIT_SSH_COMMAND</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"ssh -i ~/.ssh/aur -o IdentitiesOnly=yes -o StrictHostKeyChecking=no"</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">run</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">git clone ssh://aur@aur.archlinux.org/trello-cli.git aur-repo</span></div><div class="line"></div><div class="line"><span style="color: var(--shiki-color-text)"> - </span><span style="color: var(--shiki-token-keyword)">name</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">Check if update needed</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">id</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">check</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">working-directory</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">aur-repo</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">run</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">|</span></div><div class="line"><span style="color: var(--shiki-token-string)"> CURRENT=$(grep '^pkgver=' PKGBUILD | cut -d= -f2)</span></div><div class="line"><span style="color: var(--shiki-token-string)"> if [ "$CURRENT" = "$" ]; then</span></div><div class="line"><span style="color: var(--shiki-token-string)"> echo "Already up-to-date (version $CURRENT)"</span></div><div class="line"><span style="color: var(--shiki-token-string)"> echo "skip=true" >> $GITHUB_OUTPUT</span></div><div class="line"><span style="color: var(--shiki-token-string)"> else</span></div><div class="line"><span style="color: var(--shiki-token-string)"> echo "Update available: $CURRENT -> $"</span></div><div class="line"><span style="color: var(--shiki-token-string)"> echo "skip=false" >> $GITHUB_OUTPUT</span></div><div class="line"><span style="color: var(--shiki-token-string)"> fi</span></div><div class="line"></div><div class="line"><span style="color: var(--shiki-color-text)"> - </span><span style="color: var(--shiki-token-keyword)">name</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">Update PKGBUILD</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">steps.check.outputs.skip != 'true'</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">working-directory</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">aur-repo</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">run</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">|</span></div><div class="line"><span style="color: var(--shiki-token-string)"> sed -i "s/^pkgver=.*/pkgver=${{ steps.npm.outputs.version }}/" PKGBUILD</span></div><div class="line"><span style="color: var(--shiki-token-string)"> sed -i "s/^pkgrel=.*/pkgrel=1/" PKGBUILD</span></div><div class="line"><span style="color: var(--shiki-token-string)"> sed -i "s/^sha512sums=.*/sha512sums=('${{ steps.checksum.outputs.sha512 }}')/" PKGBUILD</span></div><div class="line"></div><div class="line"><span style="color: var(--shiki-color-text)"> - </span><span style="color: var(--shiki-token-keyword)">name</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">Update .SRCINFO</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">steps.check.outputs.skip != 'true'</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">working-directory</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">aur-repo</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">run</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">|</span></div><div class="line"><span style="color: var(--shiki-token-string)"> useradd -m builder</span></div><div class="line"><span style="color: var(--shiki-token-string)"> mkdir -p /home/builder/build</span></div><div class="line"><span style="color: var(--shiki-token-string)"> cp -a . /home/builder/build</span></div><div class="line"><span style="color: var(--shiki-token-string)"> chown -R builder:builder /home/builder/build</span></div><div class="line"><span style="color: var(--shiki-token-string)"> runuser -u builder -- bash -lc 'cd /home/builder/build && makepkg --printsrcinfo > .SRCINFO'</span></div><div class="line"><span style="color: var(--shiki-token-string)"> cp /home/builder/build/.SRCINFO ./.SRCINFO</span></div><div class="line"></div><div class="line"><span style="color: var(--shiki-color-text)"> - </span><span style="color: var(--shiki-token-keyword)">name</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">Commit and push</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">steps.check.outputs.skip != 'true'</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">working-directory</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">aur-repo</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">env</span><span style="color: var(--shiki-token-keyword)">:</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">GIT_SSH_COMMAND</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"ssh -i ~/.ssh/aur -o IdentitiesOnly=yes -o StrictHostKeyChecking=no"</span></div><div class="line"><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">run</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">|</span></div><div class="line"><span style="color: var(--shiki-token-string)"> git config user.name "Michael Heap"</span></div><div class="line"><span style="color: var(--shiki-token-string)"> git config user.email "m@michaelheap.com"</span></div><div class="line"><span style="color: var(--shiki-token-string)"> git add PKGBUILD .SRCINFO</span></div><div class="line"><span style="color: var(--shiki-token-string)"> git commit -m "Update to version $"</span></div><div class="line"><span style="color: var(--shiki-token-string)"> git push</span></div></code></div></pre>
<p>Now you have a workflow that will update your <code>PKGBUILD</code> automatically once per week, or whenever you trigger the action manually.</p>
Yes, if…2026-02-04T20:25:58Zhttps://michaelheap.com/yes-if/<p>People love hearing “yes.”</p>
<p>In a meeting, it sounds like momentum. In Slack, it reads as alignment. On a roadmap, it feels like progress.</p>
<p>It's also the first step towards disappointment. "Yes" is a contract - a commitment to deliver something. If you say "yes" to everything, you start paying for it with late nights, dropped balls and a creeping resentment that's hard to admit because you <em>chose</em> to say yes.</p>
<p>Every “yes” goes on an invisible ledger. Time, attention, credibility. All quietly debited, rarely reconciled. Eventually you <em>will</em> go bankrupt and disappoint someone. The only question is whether you disappoint them when they ask, or later when your lack of delivery has a much higher cost.</p>
<h2 id="the-two-types-of-%22yes%22" tabindex="-1">The two types of "yes"</h2>
<p>We say “yes” for many reasons. In personal relationships, it can strengthen bonds and make others feel valued. In professional settings, saying “yes” can signal teamwork, ambition, and a commitment to shared goals. However, saying “yes” can also be driven by <em>fear</em>. A fear of disappointing others, a fear of missing out, or a fear of conflict.</p>
<p>No matter <em>why</em> we say yes, the expectation to be agreeable leads to us committing to way more than we can ever hope to achieve. You say yes to avoid disappointing someone, only to disappoint them at the worst possible time: after you've agreed, they've told other people about their idea and half of the org is excited.</p>
<p>So, how do you keep the momentum that "yes" brings without overcommitting yourself? By using "Yes, if."</p>
<h2 id="%22yes%2C-if%22-is-a-trade" tabindex="-1">"Yes, If" is a trade</h2>
<p>“Yes” pretends there is no conversation. It hides the cost and defers the pain.</p>
<p>“No” without context shuts the conversation down before anything useful is learned.</p>
<p>“Yes, if” <em>is</em> the conversation.</p>
<p>“Yes, if” makes the cost visible. It takes the invisible tax you were about to pay and puts it on the table where everyone can see it. Not as a complaint. Not as drama. As accounting.</p>
<ul>
<li>“Yes, if you can wait until Thursday after the report goes out.”</li>
<li>“Yes, if we agree this replaces the other thing I’m doing.”</li>
<li>“Yes, if you do the first draft and I’ll review.”</li>
<li>“Yes, if we can fund the work and assign an owner.”</li>
</ul>
<p>Most frustration at work doesn’t come from disagreement. It comes from ambiguity.</p>
<h2 id="applying-this-in-real-life" tabindex="-1">Applying this in real life</h2>
<p>A good “Yes, if” is just good bookkeeping. It names which account you’re drawing from.</p>
<ul>
<li>Time: “Yes, if it can wait until Thursday.”</li>
<li>Priority: “Yes, if Bob agrees this outranks the report.”</li>
<li>Ownership: “Yes, if you draft it and I’ll review.”</li>
<li>Scope: “Yes, if we do a 1-page version, not a full report.”</li>
<li>Resourcing: “Yes, if we fund it / staff it.”</li>
</ul>
<p>Most org pain comes from pretending those currencies are infinite. By explicitly naming them and negotiating with the requester you can be collaborative without overcommitting.</p>
<h2 id="price-the-work" tabindex="-1">Price the work</h2>
<p>“Yes, if” is about maintaining balance. It’s about making sure the work you agree to aligns with your time, energy, and priorities.</p>
<p>Next time someone asks you for something, name the trade-offs. Make the constraint explicit. Price the work. See if they still want it.</p>
<p>If that trade kills the request, it wasn’t a priority. It was just an idea looking for someone else to carry the cost.</p>
<p>“Yes, if” is about maintaining balance. It's about making sure that projects align with your time, energy, and priorities. It fosters collaboration without compromising your well-being. Next time you’re faced with a request, remember: you don’t have to say “yes” to everything, but with the right conditions in place, you might just find a way to make it work for everyone.</p>
Delivery and Dopamine2026-01-28T20:08:57Zhttps://michaelheap.com/delivery-and-dopamine/<p>I've built my career around being the person that delivers. Throw me into a messy problem and I'll figure it out and make progress.</p>
<p>My performance reviews praised output. Peers trusted me as the person who “got things done”. The system rewarded shipping, and for a long time I used that as my definition of “doing my job well”. I confused visibility with value, and if I’m being totally honest, I was addicted to being the person that everyone pinged.</p>
<p>Then one day, my career stalled. I just couldn't justify another promotion on output alone. Shipping things feels good in a way that designing systems doesn't. Shipping provided that immediate dopamine hit, but system design doesn't work like that. Feedback is delayed and ambiguous, and honestly, things usually get worse before they get better.</p>
<p>You can justify being a player-coach as a manager, senior manager and in some places, even a director. But at some point you need to accept that the best use of your time is to act as a multiplier for the team that you're supporting.</p>
<p>That's where I failed.</p>
<h2 id="dopamine-vs.-leverage" tabindex="-1">Dopamine vs. Leverage</h2>
<p>Execution gives you fast feedback. System design demands patience.</p>
<p>When I say “designing systems,” I don’t mean architecture diagrams. I mean the interfaces that shape how the business operates: who makes decisions, how work gets planned and what happens when things break.</p>
<p>Discipline means letting the system converge on an idea and make decisions without you "helping". It means allowing systems to fail without heroics, enabling the organization to understand the flaws in the system. It means trading that dopamine hit for organizational leverage.</p>
<p>It also means being comfortable with feeling like a failure. Letting projects fail felt irresponsible. Not being hands on felt lazy. And if I'm honest, not getting that dopamine hit felt like withdrawal.</p>
<h2 id="the-relapse" tabindex="-1">The Relapse</h2>
<p>When leaders step back, things slow down. Metrics wobble. People complain.</p>
<p>That's the system revealing itself. Heroics are praised, and organizations choose short-term relief over long-term leverage. So when things got messy, I didn’t <em>choose</em> execution, I relapsed into it. It was the easy choice. The safe choice. It was also the wrong choice.</p>
<p>I fell back into being an IC multiple times. Each time the dopamine hit was even bigger. "Things were really going downhill and you turned it around!" Each time, it was even harder to crawl back out of the routine of shipping and start thinking about the overall system.</p>
<p>In one example, there was a last minute tooling update required for a key customer. So I dove in, made the changes, got the relevant approval and shipped a fix within 24 hours. The customer was happy, the account team was happy, the engineering team appreciated not being interrupted... then less than a week later the <em>exact</em> same thing happened again. Everyone's first instinct was to message me. My save the previous week didn’t fix the problem; it taught the organization the workaround.</p>
<h2 id="the-%22helping%22-trap" tabindex="-1">The "helping" trap</h2>
<p>I told myself I was "just helping".</p>
<p>But “helping” was a trap. It kept me focused on solving the immediate problem instead of asking why this problem kept showing up. It made me the bottleneck instead of fixing the decision boundary. It let me feel needed, and in the process <strong>I stole learning from the organization</strong>. The team didn’t get stronger; the system didn’t improve; I just became the workaround.</p>
<p>I'm not <em>entirely</em> at fault, though. While I was chasing the next hit I was also operating in an environment that was easy to understand. Do good work, get rewarded. Designing systems is filled with ambiguity and risk. System design is political, cross-functional and uncertain. It's also invisible to your stakeholders (until it fails). Execution is visible, measurable and safe.</p>
<h2 id="build-a-system-that-ships-without-you" tabindex="-1">Build a System That Ships Without You</h2>
<p>Building a sustainable system is thankless work. The feedback loop is slow, and the wins are quiet. You don’t measure it by what <em>you</em> shipped - you measure it by what happens when you’re not there.</p>
<p>A system is getting healthier when:</p>
<ul>
<li><strong>Recurring fires disappear</strong> because you removed root causes instead of treating symptoms.</li>
<li><strong>Decisions happen without you</strong> (and they’re good enough. Not <em>perfect</em>. Good enough).</li>
<li><strong>Approval paths are explicit</strong>: who decides, what “good” looks like, and how long each step should take.</li>
<li><strong>Leading indicators show up early</strong>, so problems surface while they’re still cheap to fix.</li>
</ul>
<p>If you're looking for a concrete example, we moved approvals from an ad-hoc Slack-based approach with emoji reactions to a weekly forum with named decision owners and SLAs. It <em>felt</em> like more process, but the overall time investment was much lower.</p>
<p>To understand if the system is working, ask yourself "if I take a month off, does the team still make good decisions, ship on time and recover from setbacks?".</p>
<p>(Fun fact! This is one of the reasons I quit my last job. I took 3 months off when my baby was born and when I got back to work it was as though I'd been there the whole time. They no longer needed me, and it was time to go and build systems somewhere else.)</p>
<h2 id="the-organization-trained-me" tabindex="-1">The organization trained me</h2>
<p>The org rewarded heroics, and I cashed the checks. Repeatedly.</p>
<p>In many cases a leader is doing what the organization expects of them. It's not that they can't let go of execution, it's that no-one tells them that they should. So we keep delivering, getting that dopamine hit and feeling effective.</p>
<p>Until organizations realize that leadership is about designing systems that enable the work, rather than doing it, they'll keep celebrating productivity and wondering why nothing scales.</p>
<p>We have to fix the incentives. Evaluate leaders based on team throughput and decision quality, not individual output. Celebrate recurring fires that no longer happen. Put “eliminated dependencies” and “decisions made without me” into performance reviews. Heroic saves should result in a postmortem and a system fix, not a medal.</p>
<p>If heroics are the path to praise, leaders will keep relapsing (even when they know better). If you keep rewarding people for <em>being the system</em> rather than <em>building the system</em> you won’t get leaders.</p>
<p>You’ll get bottlenecks with better titles.</p>