Thursday, July 16, 2026

The Power of Guilds

For a technical enterprise, collaborative communication is one of the most powerful tools leaders have to enact meaningful change in the presence of silos. How many times have you as an enterprise leader heard something along the lines of...

  • "You built what? We solved problem that two years ago."
  • "I don't know what team owns that service."
  • "Every application logs differently."
  • "Only two people really understand that system."
  • "We have four different authentication APIs."

Technical silos develop naturally through segmented responsibilities and a lack of architectural governance. If there is no business reason for teams to talk, the flow of ideas outside each team can effectively stop. Most of us have felt the silo problem at least once in our technical careers, so how do you start to solve it?

I had a very good success story last year when I resurrected an architectural guild. The premise was simple: make space for connection and communication, invite some business teams to talk about what they are doing, seed it with some questions and problems that are visible from a leadership level, and give the smart people you've hired a forum to talk. 

The benefits I personally saw were striking. I could see them in the collaborations that grew, the common issues that surfaced, the solution sharing, and the alignment that started forming across teams. We were on track to build at least three independent identity solutions before we stood up a team to build a proof of concept around one common solution. Quantifying the value difference on something like that is remarkably difficult, but the long-term enterprise benefit is easy to understand.

How would I go about standing up something like this again? I would do the following:

  • Assign a high-level leadership sponsor: This role will add legitimacy, push the meeting forward, and keep it focused. Their responsibility is to ask leading questions, fill the awkward silence, capture common friction to solve at a higher level, and reach out to other verticals to present.
  • Carve out calendar time: not Friday at 4 p.m. and not during lunch. In my experience, an hour was a good container.
  • Make attendance voluntary: Leaders should stress why participating in the forum matters and create the space for people to attend so the group can deliver real value. If you create value, they will come.
  • Invite the right audience: A cross-functional cohort of business and technical representatives helps keep the discussion balanced and focused on enterprise value. There is nothing like having a group of engineers talk through a solution and then hearing a business partner say, "That is not the part of the problem we need to solve."
  • Avoid mandating outcomes. You want to establish a safe space for communication and connection, not create another place where people have to report on assigned work items. There are better methods for driving mandates without killing the vibe of the forum.

The larger leadership lesson is simple: communication forums do not solve silos by themselves, but they create the conditions to help alleviate the problems they cause. When leaders make those conversations possible, it can unlock value opportunities across the enterprise.


Tuesday, July 14, 2026

The AI Augmented Leader

When does AI come for the leaders?

So much of the focus around the AI discussion has been on what roles and jobs will just be automated out of existence. I keeping seeing comments that it’s coming for leaders and given the upheaval in the industry over jobs that were considered irreplaceable just a few years ago, I can’t help but think it’s now well within the realm of possibility.

Picture of the Terminator

But instead of wondering, "Is AI coming for leadership jobs?", I really want to change the premise and ask, "If AI can handle much of the tedium that managers do today, how should the higher-value work of a leadership role change in response?"

That's a strategic question every executive team should be wrestling with right now.

Question to the reader:
What’s one attribute or skill that will create more value in an AI augmented leader?

How does the role of an AI augmented leader change?

AI-augmented leadership will not simply make existing leaders faster; it will change what organizations value in leadership. As administrative work becomes easier to automate, the differentiator shifts from producing artifacts to setting direction, improving decisions, and creating the conditions for people to do meaningful work.

For decades, organizations may have unintentionally rewarded leaders for activities that consumed time rather than created value. Many leaders became a combination of administrator and decision maker, with status reports, forecasts, documentation, and project plans treated as routine parts of the role. As AI democratizes those activities, it may expose the difference between someone who manages work and someone who creates direction.

That possibility will create some discomfort because it implies career disruption for people whose value has been tied to administration and process ownership. It also creates an opportunity where leaders should have more time to focus on the higher-value work that only people can perform.

The leadership role begins to look more like decision management: ensuring the organization is moving in the right direction, not merely moving faster. As AI accelerates the pace of work, the cost of accelerating toward the wrong objective becomes more significant. This means skills like being able to ensure the correct problems are being solved, managing competing priorities and resultant tradeoffs, and building the necessary culture, become forefront activities in the new AI augmented leadership role.

Question to the reader:
What traits will best support a leader in a more decision-centric role?

What will differentiate exceptional leaders in the post-AI era?

In the previous section, I explored how AI changes the leadership role. Now, let's consider what differentiates exceptional leaders once AI removes much of the management toil.

I would argue that pure leadership traits will get amplified in importance to an organization. Qualities such as Integrity, Judgement, Vision, Communication, Empowerment, and Adaptability will remain as high-value qualities that drive high-performing organizations.

There is something reassuring about that, as the qualities that made leaders effective before AI are the same qualities that can make them exceptional after it. If leaders focus on those core competencies, and AI relieves some of the management toil, they will have more space to lead an AI-enabled workforce.

Let's take at each trait more closely. Specifically, what the trait looks like, how AI can augment it, and what the leader must still own.

Integrity

Trust is built when a leader's actions consistently match their words and actions. Trust is a cornerstone of an effective team. Just look at any military Special Ops team, they operate as a high-trust unit and combined with training is what makes them super effective.

AI can surface when a leader's actions drift from their stated principles or commitments, but only the leader can choose the consistent behaviors that earn and sustain the trust of their team. 

Judgment

The ability to make sound decisions with incomplete information, which is a form of big picture strategic planning. Being able to distinguish between good and bad decisions, and then own that decision, is a form of building trust. It's not about always being right, it's about consistently making decisions that move the organization forward while learning quickly when circumstances change. 

AI can augment judgement through analyzing data, and modeling scenarios, which allows the leader to apply their instinct and experience to make the principled decisions amid uncertainty.

Vision

Visionary leadership involves creating a compelling vision of the future that inspires and motivates others to act toward its realization. Leaders who provide a clear picture of where the organization is going and why, empower the team to get on the same page and act toward the same goals. 

AI can augment vision through drafting strategies, and highlighting trends, which allows the leader to focus on defining purpose and direction and how to align the organization toward the common goals.

Communication

Being able to articulate vision and strategy clearly while inspiring and motivating their team to work towards a common goal, is a crucial skill for an effective leader. But there is a corollary, a leader must also be able to ask great questions which means being a great listener. They need to show that they are curious and ask for ideas, input, feedback, and suggestions. They’re constantly pushing and probing to learn more. 

AI can help leaders tailor messages, summarize feedback, and identify communication gaps, but the leader applies communication by actively listening, asking thoughtful questions, and creating meaningful dialogue that builds the connection and clarity that drive effective communication.

Empowerment

Great leaders create environments where others can succeed. They delegate authority, develop talent, remove obstacles, and encourage ownership rather than micromanagement. Their success is measured by the growth and performance of their teams, not by their own individual contributions. Exceptional leaders push themselves and others to exceed expectations by fostering this growth mindset. 

AI can identify opportunities for delegation, coaching, and skill development, but the leader will apply empowerment by trusting others with meaningful responsibility, removing barriers to success, and creating a culture where people are encouraged to grow beyond their perceived limits.

Adaptability

Business conditions, technologies, and customer expectations continually evolve. Effective leaders remain curious, challenge assumptions, and adjust strategy without losing sight of long-term objectives. Adaptability is especially important during periods of uncertainty and disruption, such as in our current AI driven upheaval.

AI can augment adaptability by rapidly detecting emerging trends, risks, and opportunities. The leader applies adaptability by challenging assumptions, making thoughtful strategic adjustments, and giving people the confidence to navigate change together.

These aren't the only important traits, but they are foundational because they influence leadership direction and effectiveness at a fundamental level. AI can improve how leaders perform their responsibilities, but only leaders determine why they act, what they prioritize, and who they develop.

Conclusion

The leaders who thrive in this next era will not be the ones who simply use AI to do more work faster. They will be the ones who use AI to create more space to support their leadership. As the routine work of management becomes easier to automate, leadership becomes more human, not less. The opportunity is to let AI handle more of the toil so leaders can focus on the work that matters most: setting direction, developing people, and building organizations capable of making better decisions in a faster-moving world.



Friday, April 24, 2020

Benchmarking in .NET

I had a recent chance to code review a hot fix for our platform at work. One particular changeset caught my attention as it had a funny code smell. The method was removed in the final changeset due to a requirements change, but I still wanted to understand the ramifications of that code and see if there was something there. So, let's do some benchmarking...

Using BenchmarkDotNet

I had been introduced to this package last year, and was impressed after using it to profile some new code my team had written. BenchmarkDotNet is an open source profiling framework which allows you to build performance measuring projects into your solutions very easily. You simply add a new project, pull in the NuGet package, and start writing tests. A neat feature of this package is that it allows you to also get memory diagnostics, so you can understand the GC footprint of your code.

Name that Smell

Here is method that piqued my curiosity.


public IList<ResultMessage> CheckLoadFederalStimulusRules(int code, AccountStatusReasonCode reason)
{
    var allowedLockedReasons = new List<accountstatusreasoncode>
    {
        AccountStatusReasonCode.PASSWORD_ATTEMPTS,
        AccountStatusReasonCode.PIN_ATTEMPTS,
        AccountStatusReasonCode.UNLOCK_ATTEMPTS
    };
    var messages = new List<ResultMessage>();
    switch (code)
    {
        case (int)AccountStatusCode.Closed:
        case (int)AccountStatusCode.PermanentlyClosed:
            messages.Add(new ResultMessage(ResultStatus.Fail, AccountStatusCode.Closed));
            break;
        case (int)AccountStatusCode.Locked:
            if (!allowedLockedReasons.Contains(reason))
            {
                messages.Add(new ResultMessage(ResultStatus.Fail, AccountStatusCode.Locked));
            }
            break;
    }
    return messages;
}

This is a really simple method. It creates a list of allowable reasons, and then verifies if the operation is allowed. Knowing that the method will get called a lot, the memory allocations just didn't sit right. So I dig in to see if I can make it faster.

Build the Benchmark Harness

Typically you would want to build this right into your solution, but I use Visual Studio for Mac at home and this solution doesn't play nice without IIS, so I go an alternate route and setup a new .NET Core console app.

public class Program
{
    TestClass test = new TestClass();

    static void Main(string[] args)
    {
        Console.WriteLine(@"Let's test some code!");
        var summary = BenchmarkRunner.Run<Program>();
    }

    [Benchmark]
    [Arguments(0, AccountStatusReasonCode.NONE)]
    [Arguments(0, AccountStatusReasonCode.UNLOCK_ATTEMPTS)]
    [Arguments(1, AccountStatusReasonCode.UNLOCK_ATTEMPTS)]
    [Arguments(1, AccountStatusReasonCode.NONE)]
    [Arguments(3, AccountStatusReasonCode.UNLOCK_ATTEMPTS)]
    [Arguments(3, AccountStatusReasonCode.NONE)]
    public void TestOriginal(int code, AccountStatusReasonCode reason)
    {
        var messages = test.CheckLoadFederalStimulusRules(code, reason);
    }
}

The first thing I do is tear the method out of it's home, throw it into the test harness, and setup a benchmark to exercise the various code paths. The attributes drive the benchmarks and allow the developer to control how any particular method is tested. I wanted each code path evaluated to make sure I get a full idea of how the method behaves. Running the application kicks off the profiling process and dumps the diagnostics out to the console. Since I don't have anything to compare it to, I run through some simple optimizations just to see how much better we can get.

Let's Tune Some Code

I wrote three different versions of the method to get a feel for how each optimization affects the performance. I'll throw the code for all of them at the bottom of the page.

UpdatedCheckLoadFederalStimulusRules
I moved the List<AccountStatusReasonCode> object to a static class variable, so we only instantiate and initialize the list once.

OptimizedCheckLoadFederalStimulusRules
This version was a lot more aggressive. I noticed I could take advantage of the calling class where it treats a null response object as a success condition. Next, I unwound the switch block and turned it into if/then statements.

FinalCheckLoadFederalStimulusRules
This version also uses the null return to prevent creating an empty response list for success conditions. It also happens to be much more readable than the Optimized version above.

Benchmarking & Results

BenchmarkDotNet produces a bunch of statistical analysis graphs and files in the application folder, but I am really just interested in the console output for my purposes. The output breaks down each method and each test case and shows how it performed under load.


The results were actually a bit surprising and show just how expensive memory allocations are. For the fastest path, the final version produced a 91% decrease in runtime with no memory allocations. For the slowest path, we still produced a 26% decrease in runtime, and cut the amount of garbage collection almost in half.

The difference between the Optimized version and the Final version showed that they are almost equivalent, but to have the code be much more readable and maintainable is worth the tiny performance hit in the fastest code paths. For a little more insight into the underlying difference between these two methods, refer to this article on the differences between if and switch at the IL level.

Final Thoughts and Code

This simple exercise just goes to show how small items can eat into the performance of your application, and also how expensive memory allocations are. With just a little more care and attention by the original coder, we could have had a version that performed much better under load. The hope is that you can utilize this package to test the code you are writing and experiment with various constructs to better understand how to optimize your application and identify performance problems before your code hits the field.

Here is the code file in it's final tested form.

using System;
using System.Collections.Generic;
using Serve.Internal.Shared.DataContracts;

namespace Apr2020
{
    public class TestClass
    {
        static List<AccountStatusReasonCode> AllowedLockReasons;

        #region Constructors
        public TestClass()
        {
        }

        static TestClass()
        {
            AllowedLockReasons = new List<AccountStatusReasonCode>
            {
                AccountStatusReasonCode.PASSWORD_ATTEMPTS,
                AccountStatusReasonCode.PIN_ATTEMPTS,
                AccountStatusReasonCode.UNLOCK_ATTEMPTS
            };
        }
        #endregion

        // This is the original version of the method
        public IList<ResultMessage> CheckLoadFederalStimulusRules(int code, AccountStatusReasonCode reason)
        {
            var allowedLockedReasons = new List<AccountStatusReasonCode>
            {
                AccountStatusReasonCode.PASSWORD_ATTEMPTS,
                AccountStatusReasonCode.PIN_ATTEMPTS,
                AccountStatusReasonCode.UNLOCK_ATTEMPTS
            };
            var messages = new List<ResultMessage>();
            switch (code)
            {
                case (int)AccountStatusCode.Closed:
                case (int)AccountStatusCode.PermanentlyClosed:
                    messages.Add(new ResultMessage(ResultStatus.Fail, AccountStatusCode.Closed));
                    break;
                case (int)AccountStatusCode.Locked:
                    if (!allowedLockedReasons.Contains(reason))
                    {
                        messages.Add(new ResultMessage(ResultStatus.Fail, AccountStatusCode.Locked));
                    }
                    break;
            }
            return messages;
        }

        // We use the static collection for checking lock reasons and lessen memory allocations
        public IList<ResultMessage> UpdatedCheckLoadFederalStimulusRules(int code, AccountStatusReasonCode reason)
        {
            var messages = new List<ResultMessage>(1);
            switch (code)
            {
                case (int)AccountStatusCode.Closed:
                case (int)AccountStatusCode.PermanentlyClosed:
                    messages.Add(new ResultMessage(ResultStatus.Fail, AccountStatusCode.Closed));
                    break;
                case (int)AccountStatusCode.Locked:
                    if (!TestClass.AllowedLockReasons.Contains(reason))
                    {
                        messages.Add(new ResultMessage(ResultStatus.Fail, AccountStatusCode.Locked));
                    }
                    break;
            }
            return messages;
        }

        // Unwind the case statement and try and take advantage of the null
        // return handling here to shortcut processing
        // Less readable, but very optimized
        public IList<ResultMessage> OptimizedCheckLoadFederalStimulusRules(int code, AccountStatusReasonCode reason)
        {
            if ((int)AccountStatusCode.Open == code)
            {
                return null;
            }
            if ((int)AccountStatusCode.Locked == code)
            {
                if (!TestClass.AllowedLockReasons.Contains(reason))
                {
                    var messages = new List<ResultMessage>(1);
                    messages.Add(new ResultMessage(ResultStatus.Fail, AccountStatusCode.Locked));
                    return messages;
                }
            }
            else if ((int)AccountStatusCode.Closed == code || (int)AccountStatusCode.PermanentlyClosed == code)
            {
                var messages = new List<ResultMessage>(1);
                messages.Add(new ResultMessage(ResultStatus.Fail, AccountStatusCode.Closed));
                return messages;
            }
            return null;
        }

        // Back to the Switch construct, optimized for readability and speed
        public IList<ResultMessage> FinalCheckLoadFederalStimulusRules(int code, AccountStatusReasonCode reason)
        {
            switch (code)
            {
                case (int)AccountStatusCode.Closed:
                case (int)AccountStatusCode.PermanentlyClosed:
                    {
                        var result = new List<ResultMessage>(1);
                        result.Add(new ResultMessage(ResultStatus.Fail, AccountStatusCode.Closed));
                        return result;
                    }
                case (int)AccountStatusCode.Locked:
                    {
                        if (!TestClass.AllowedLockReasons.Contains(reason))
                        {
                            var result = new List<ResultMessage>(1);
                            result.Add(new ResultMessage(ResultStatus.Fail, AccountStatusCode.Locked));
                            return result;
                        }
                        return null;
                    }
                default:
                    return null;
            }
        }
    }
}