Thursday, July 23, 2026

Software Craftsmanship — Why Testing Matters More Than Ever

A few weeks ago, I wrote a post called SoftwareCraftsmanship — When AI Writes the Code. The basic idea was pretty simple: AI hasn’t made software craftsmanship disappear. It has moved the craft from writing every line ourselves to making sure we understand the problem and own the outcome. That part is not really new.

For most of my career, we crafted code by taking requirements, breaking them down into logic, and turning that logic into instructions a computer could execute. AI is changing that because it can generate code fast enough that writing code is no longer the hard part, regardless of the size or complexity of the ask.

On the surface, that sounds like pure upside: higher productivity, more output, shorter cycle times. But software delivery has never been just about producing code. It is about delivering value to customers through software they can trust. The hard part was rarely just writing the code. It was understanding the problem, deciding what success looked like, and proving that the software solved the right problem.

AI can make code generation faster, but it does not remove the work on either side of it. The more it helps us generate software, the more we have to prove that the software works as intended. That sounds obvious. I’m not sure we’ve started acting like it yet.

I was reading Leah Brown's article How to Work with AI Coding Tools Without Losing Your Engineering Judgment, and this line jumped out at me:

"They do not produce correct answers—they produce statistically likely answers, which is a different thing."

That is an important distinction. Because generative AI systems are probabilistic rather than deterministic, they are incredibly good at producing answers that look right. Sometimes they are right. Sometimes they're not. The tricky part is that both correct and incorrect answers are often presented by an AI with the same level of confidence.

Later in the article, Brown asks a question that I think every engineer should get comfortable asking:

"What observable evidence would confirm that this suggestion is actually correct, and have I gathered it?"

I think that's the question that as engineers we need to get better at asking.

Not "Does this code look reasonable?"

Not "Would I have written it this way?"

Instead, the better question is: what evidence do I have that this is actually correct?

That is not just my concern. The industry is starting to see the same issue.

Google's DORA team talks about a "verification tax" that organizations pay as they adopt AI-assisted development. As code generation gets faster, teams spend more effort reviewing, validating, and proving that generated code actually does what it is supposed to do.

That same pattern shows up in the 2025 DORA research, which summarizes it this way: "Without robust control systems, like strong automated testing, mature version control practices, and fast feedback loops, an increase in change volume leads to instability." That is the risk AI exposes if our delivery pipelines cannot handle the volume of change in an AI-native world.

The Octopus Deploy AI Pulse Report describes something similar. Teams are seeing real productivity gains from AI, but they're also discovering that testing, reviews, deployment processes, and delivery systems don't automatically speed up just because code generation does.

In other words, AI can help us produce code faster. It does not automatically help us prove that the software works. That proof still has to come from engineering discipline, automated validation, and the delivery systems around the code.

That lines up with the way IT Revolution described the downstream problem: "The current constraint in the delivery cycle is usually somewhere downstream of the coding effort: code review, integration, system test, or some other verification or validation step." If that is true, then writing more code faster does not help much unless the validation system gets stronger and more automated too.

Testing Is the Safety Net Now

This is not a new idea. Integration and system tests in the deployment pipeline have always mattered. What AI changes is how little room we have left to pretend they are optional. If AI helps us generate code faster, automated validation cannot stay a "we'll add that later" item. For new systems, that means building it in from the start. For existing systems, it means moving automated validation into the delivery path now, before manual QA becomes the bottleneck.

As AI makes writing code cheaper, unit tests are not enough. Unit tests can tell us whether a function behaves the way we expect. They do not tell us the system works. Automated integration and system tests in the deployment pipeline are the safety net for delivering applications in the AI-native era.

Those tests need to run before production. And if they fail, that should pull the Andon Cord: make the problem visible, stop the pipeline, and get the right people swarming on it immediately. Not warn us politely. Not create a ticket for somebody to look at someday. Stop the pipeline until the safety net is green again.

This also changes what we need to pay attention to in code review. It has been easy to treat test code as a second-class citizen in the code base because it is not the actual product code. We review the product code carefully, then give the tests a lighter pass as long as they exist and seem reasonable.

Tests are code. They do not stay useful because we wrote them once. Every new feature, every meaningful change, and every bug fix should leave the pipeline with better coverage than it had before. If the product keeps changing and the tests do not, the safety net slowly stops protecting the system we are shipping.

That does not work anymore. If tests are what we are relying on to validate AI-assisted changes, then tests need to get at least the same scrutiny as the product code, if not more. First, we need to know the system works on the happy path. If we do not have that, the risk is not that we missed an edge case. The risk is that the system fails and nothing in the pipeline catches it. Even when the pipeline is green and all the checks pass, the safety net can still have holes in it.

That is the part that gets more important in an AI-assisted world. Testing is not becoming more important because AI writes bad code. Testing is becoming more important because AI writes a lot of code, and it makes an old problem harder to ignore. We have always been able to write code faster than we can prove it works.

That proof cannot depend on manual QA catching up after the fact. It has to be built into the delivery system. The pipeline needs to keep asking the same blunt question before we ship: does the product still work the way we think it does?

Unit Tests Still Matter, But They Are Not Enough

None of this makes unit tests less useful. It makes them less sufficient. Unit tests still matter because they are fast, close to the code, and good at helping us understand small pieces of behavior. They can catch regressions while the change is still small enough to understand, and they can make debugging easier without forcing us to reason through the whole system every time.

But unit tests are not a substitute for integration and system tests. They can give us fast feedback that small pieces behave correctly in isolation. They cannot show that the product works when those pieces are wired together, deployed, configured, authenticated, authorized, and used the way customers use it. Unit tests are still worth writing. They help us move faster safely only when they sit alongside integration and system tests that prove the product still works.

What We Need to Do Now

AI is not making software engineering easier. It is making some parts faster and other parts harder to ignore. Testing is one of those parts.

If we are going to use AI to create more code, we need delivery pipelines that can handle more change. That means automated integration and system tests that run before production. It means failed tests stop the pipeline. It means tests get reviewed like code because they are code.

It also means treating tests as part of every feature, every meaningful change, and every bug fix. Not someday. Not after the release. As part of the work. If the product changes and the tests do not, the pipeline gets less trustworthy over time.

As the 2025 DORA research puts it, AI's primary role is "as an amplifier, magnifying an organization's existing strengths and weaknesses." If the pipeline is slow, fragile, or mostly dependent on manual validation, AI will just move the bottleneck downstream faster. The teams that benefit most from AI will not be the ones that generate the most code. They will be the ones that can safely absorb more change because their pipelines give them fast, honest feedback about the current state of the product.


References

·        Leah Brown, How to Work with AI Coding Tools Without Losing Your Engineering Judgment, IT Revolution: https://itrevolution.com/articles/how-to-work-with-ai-coding-tools-without-losing-your-engineering-judgment/

·        Jonathan Snyder, Triangulating for Truthiness: Effective Engagement with LLMs for Software Development and Operations, IT Revolution: https://itrevolution.com/product/triangulating-for-truthiness/

·        GeeksforGeeks, Deterministic vs Probabilistic AI: Why Modern Models Lack Predictability: https://www.geeksforgeeks.org/artificial-intelligence/ai-for-geeks-week2/

·        Google Cloud DORA, ROI of AI-Assisted Software Development: https://cloud.google.com/resources/content/dora-roi-of-ai-assisted-software-development

·        DORA, State of AI-assisted Software Development 2025: https://dora.dev/report/2025

·        Octopus Deploy, 2026 AI Pulse Report: https://octopus.com/publications/ai-pulse-report

·        DevLead.io, DevTips: Andon Cord: https://devlead.io/DevTips/AndonCord

Tuesday, June 16, 2026

Software Craftsmanship — When AI Writes the Code

For decades, it didn't really matter what your title was: programmer, engineer, developer.

Our titles changed, the languages we worked in changed, the stack definitely changed, but the core of the work stayed the same. We crafted code. We used code to express our intent. We took problems, broke them down into explicit logic, and told machines exactly what to do. If it worked, it was because we got the instructions right. If it didn't, we debugged until it did. That was the craft.

And it was a craft. It took skill. The kind that comes from hard-won experience. You could look at a system and see the fingerprints of the people who built it — their decisions, their trade-offs, their patterns. And depending on where you work and how far along they are with AI, those days may already be over, or at least on the way out: the days when an engineer sits down and writes every line of code by hand.

But crafts evolve. They always have. Hand typesetting gave way to the printing press. Film photography gave way to digital. Entire professions, like switchboard operators and draftsmen, were replaced or reshaped when new technology changed what the work required.

This isn't the first time a craft has shifted out from under the people practicing it. And it won't be the last. AI is doing that to software engineering right now. This doesn't make engineers obsolete. The question we need to ask is: if we're no longer crafting code, what are we crafting now?

If we used to express intent through code, what do we use now?

We don't have a clean answer yet. But some patterns are starting to show up. We're still expressing intent, just not through explicit logic. Instead of writing the exact steps, we're shaping the outcome.

A few of them are starting to look familiar:

  • We craft prompts that guide behavior instead of functions that define it
  • We assemble context instead of wiring together conditionals
  • We explicitly define what good looks like, something we used to handle by intuition
  • We curate examples and data instead of encoding every rule

None of these feel as concrete as code. They're softer. That can be uncomfortable, which is normal. The thing you were good at, the thing you built your identity around, starts to shift under your feet. Of course that feels unsettling.

Because what we craft has changed, our tools have changed too. We used to focus on IDEs, compilers, and build tools. Now we need to know how to work with prompts, agents, and context, and we need to learn them fast. Not that long ago, AI skills weren’t even a thing. Being good at the craft still means knowing how to use the tools well.

But for all of that change, one thing hasn't moved. The human side of the work is still the same. We're still trying to understand what someone actually needs. Still translating messy, ambiguous problems into something a system can work with. Still negotiating trade-offs, aligning with other people, and making judgment calls about what "good" looks like in context. That part of the craft didn't go away. If anything, it matters more now.

We're moving from crafting instructions to crafting outcomes. Our craft is becoming less about the code itself, and more about how a system behaves over time. I still read code. Until I'm no longer the one getting paged at 2 a.m., I'll keep reviewing pull requests myself.

What does this mean for junior developers?

This is where people get nervous. If the craft is changing, it's easy to wonder what's left for someone just starting out. But this isn't the end of fundamentals, and it's not a shortcut past them either.

Understanding how systems work still matters. You need to know how data flows, where things break, and how to break down a problem. Those are the skills that keep you grounded.

The risk is skipping understanding. You can build something that works without knowing why, and that doesn't hold up for long. When it crashes in production, shrugging and saying "I dunno, the AI wrote it" is not a defense. The main thing that changed is who wrote the code, not who is responsible for it, and not who still has to push it safely to production. The AI may have written the code, may have even committed it, and even created the pull request. You, the developer, are still responsible for that code. Too many people shipping code they don't understand may explain why New Relic's 2026 State of AI Coding report found that AI-generated code tends to look better in review than it performs in production.

And none of this means teams no longer matter—if anything, they matter more. You may not need the classic two-pizza team ideal that Amazon popularized under Jeff Bezos, but junior developers still need a team around them to learn how good debugging works, how people build healthy suspicion into the process, and how to develop judgment over time. AI can help individuals move faster, but software still gets built, operated, and improved by groups of people with shared context, trust, and good judgment. That lines up with Google’s Project Aristotle: team effectiveness depends less on who is on the team than on how the team works together.

The opportunity is to move fast and build understanding at the same time. Move fast, use the tools, but stay curious. When something works, ask why. When it fails, dig in. The tools will keep changing. The craft will keep shifting. The people who grow in it are the ones who learn how to understand both the system and the behavior it produces.

Automated tests now matter more than ever

That also means automated end-to-end and integration tests matter more than ever. If AI is helping us produce more code, faster, then these tests are part of what keeps us from doing something dumb at scale. But to really have value, they need to be automated and they need to run in the CI/CD pipeline. If they only run sometimes, or only when someone remembers, then they are not a safety net, just a nice intention. These tests tell us whether the system actually works when all the parts are involved, not just whether one small piece is working correctly. And reviewing those tests matters more too. If we are depending on them to help us ship safely, then they can't be treated like boilerplate or an afterthought. They need to be treated like first-class citizens. Moving forward, in a lot of cases, they matter more than unit tests. Unit tests aren't going away, but they are not the safety net they used to be. End-to-end and integration tests are often the thing standing between "looked fine in the pull request" and "why is production on fire?"

So, what do we craft now? We still craft software systems. The main difference now is who writes the code, not who is responsible for it, and not who pushes it to production. We craft clarity, judgment, and the understanding that sits between a messy human problem and a system that can act on it safely. And we still need the things that make that possible: teams that build shared context and judgment, and tests that tell us whether the system actually works. The code was never the point. It was just the medium. The medium changed. The craft didn't. What matters is that you recognize the shift and keep honing your craft.

I'd love to hear your thoughts. What does the craft look like for you right now? What's changed, what hasn't?

Further Reading

·        New Relic, 2026 State of AI Coding Report
https://newrelic.com/sites/default/files/2026-06/New-Relic-2026-AI-Code-Report-06-09-2026.pdf

·        DORA, ROI of AI-assisted Software Development
https://cloud.google.com/resources/content/dora-roi-of-ai-assisted-software-development

·        DORA, 2025 State of AI-assisted Software Development
https://cloud.google.com/resources/content/2025-dora-ai-assisted-software-development-report

·        Amazon Web Services, Amazon's Two-Pizza Teams
https://aws.amazon.com/executive-insights/content/amazon-two-pizza-team/

·        Google re:Work, Understand Team Effectiveness
https://rework.withgoogle.com/intl/en/guides/understand-team-effectiveness


Thursday, August 21, 2025

Trunk based development, This is the way

If you’ve ever been stuck in merge hell, staring at a pull request that looks more like a season finale cliffhanger than a clean diff, you know the pain of long-lived branches. It’s the dark side of Git. A better approach is trunk-based development, where you work with many short-lived branches and maintain a main branch that is always deployable. As any Mandalorian will tell you: “This is the way.”

Why Trunk-Based Development? ðŸ¤”

Trunk-Based Development (TBD) is the practice of keeping your main branch as the single source of truth, with developers committing small, frequent changes directly to it or preferably through very short-lived branches. These short-lived branches are key because they allow for pull requests and peer reviews without introducing the complexity and risk of long-lived branches. Frequent integration reduces merge conflicts, keeps the mainline healthy, and ensures that code reviews happen while the context is still fresh for both author and reviewer. 

The book Accelerate and the annual State of DevOps Report make this clear: elite-performing teams practice TBD. In fact, Accelerate explicitly calls out that teams who integrate into trunk at least daily have higher software delivery performance and lower change failure rates.

Martin Fowler reinforces this principle in his article Patterns for Managing Source Code Branches:

“The mainline should be healthy and always ready to be deployed into production with minimal effort.”

This is the essence of TBD—keeping the trunk deployable at all times.

Mandalorian - This is the way

Why Not the Alternatives? ðŸš«ðŸŒµ

There are many branching models out there—Gitflow, GitHub Flow, Mainline, Cascade—but each introduces complexity that slows delivery and increases risk. These models create bottlenecks, increase merge conflicts, and make continuous delivery harder. TBD avoids these pitfalls by keeping the codebase always releasable and reducing integration pain.

In addition to explaining trunk-based development, TrunkBasedDevelopment.com examines these alternative models and highlights the specific problems they create, reinforcing why TBD is the preferred approach for high-performing teams.

Addressing Gitflow, because many people have asked me about why it shouldn't be used: Back in 2010, Vincent Driessen introduced A Successful Git Branching Model, and it was a game-changer—for its time. It worked well for packaged software with infrequent releases. But not for modern web apps and services deploying multiple times a week or day. Driessen's edited note at the top of the post agrees with Gitflow no longer being a good fit for many situations. Gitflow is like wearing Mandalorian armor to a beach party: impressive, but wildly impractical.

ThoughtWorks’ Technology Radar warns against long-lived branches. They slow feedback loops, increase merge conflicts, and create risk. In short: they’re the Sarlacc pit of software delivery.

This Is the Way ðŸ›¡️🚀

TBD aligns perfectly with continuous delivery principles:

  • Small, frequent commits → Easier to review, easier to roll back.
  • Fast feedback → Bugs surface quickly, not weeks later.
  • Reduced merge conflicts → Because you’re not hoarding changes like a Jawa with spare parts.

And yes, feature flags are your jetpack here. They let you deploy incomplete features safely, without branching off into the Outer Rim.

How to Start Walking the Way 

  1. Short-lived branches only (think hours and days, not weeks, and never months).
  2. Automate everything: CI/CD pipelines, tests, deployments.
  3. Feature flags over feature branches.
  4. Cultural shift: Encourage collaboration and trust. Psychological safety matters when you’re committing to trunk daily.

In the words of the Armorer: ⚔️

"When one chooses to walk the Way of the Mandalore, you are both hunter and prey."

When you choose Trunk-Based Development, you’re both developer and deployer. It’s not always easy, but it leads to stronger, more resilient teams and systems.

This is the way! ðŸ›¡️✨


References:

    Wednesday, December 11, 2024

    The 2024 State of DevOps Report and the Importance of Internal Development Platforms

    On the State of DevOps Report

    The State of DevOps Report, published annually by the DevOps Research and Assessment (DORA) team, has been a cornerstone in the field of software development for over a decade. The 2024 version of the report was published this past October. The 2024 version as well as the past reports can be found here. Since its inception, the report has gathered insights from more than 39,000 professionals across various industries and organizational sizes. This extensive sample size ensures a comprehensive understanding of the DevOps landscape. The report is highly regarded for its in-depth analysis of the practices and capabilities that drive high performance in software delivery and operational efficiency. It serves as a benchmark for organizations aiming to improve their DevOps practices, providing valuable data on key performance indicators such as deployment frequency, lead time for changes, change failure rate, and mean time to recovery 6.

    The primary goals of the State of DevOps Report are to:

    • Identify Best Practices: Highlight the practices that distinguish high-performing teams.
    • Provide Benchmarks: Offer metrics that organizations can use to measure their performance.
    • Drive Improvement: Encourage continuous improvement in software delivery and operational practices.
    • Highlight Trends: Explore emerging trends and challenges in the DevOps field, such as the impact of AI and the role of platform engineering.

    In essence, the report aims to equip organizations with the knowledge and tools needed to enhance their DevOps capabilities and achieve better business outcomes.

    On Internal Development Platforms

    The 2024 State of DevOps Report highlights the growing significance of internal development platforms. These platforms are not just a trend but a cornerstone for enhancing efficiency, speed, and security in development. One of the key insights from the report is that "Teams do not need to sacrifice speed for stability" 2. Having an internal development platform helps with that.

    One of the key takeaways from the report is how internal development platforms empower developers by providing self-service tooling environments. This flexibility allows developers to work more efficiently and achieve their goals without unnecessary delays. As the report states, "Self-service tooling environments give developers the flexibility they need to work quickly and achieve their goals" 1. This empowerment is crucial for fostering innovation and maintaining a competitive edge.

    The report also emphasizes the role of internal development platforms in streamlining key processes through standardized automation. By eliminating repetitive tasks, these platforms accelerate delivery and improve overall productivity. "The full potential of DevOps is unlocked with standardized automation" 1 the report notes. This streamlined approach not only saves time but also reduces the likelihood of errors, leading to more reliable and consistent outcomes.

    Security is another critical aspect highlighted in the report. With secure tools built into the platform, internal development platforms ensure that security is integrated into the development process from the ground up. This proactive approach to security benefits everyone involved. "Security has never just been IT's job. With secure tools built into most platforms, Platform Engineering is empowering teams to build securely from the start" 1. This integration helps in mitigating risks and protecting sensitive data.

    Ultimately, the report underscores that the adoption of internal development platforms is a strategic move that supports organizational success. By fostering a culture of efficiency, speed, and security, these platforms help organizations achieve their goals more effectively. The report concludes, "The rise of developers is supported by the platform engineering team" 1. This support is vital for driving innovation and achieving long-term success in the competitive tech landscape.

    The 2024 State of DevOps Report makes it clear that self-service internal development platforms are not just beneficial but essential for software development to allow teams to move quickly and independently.

    On Paved Roads and Golden Paths

    In the realm of software development, the concepts of "paved roads" and "golden paths" have become integral to the success of internal development platforms. These terms, often used interchangeably, refer to the pre-defined, optimized workflows and best practices that streamline, and expedite, development processes, making life easier for developers and enhancing overall productivity.

    At their core, paved roads and golden paths are about providing developers with a clear, supported route to follow. As Raffaele Spazzoli from Red Hat explains, "Golden Paths are a fundamental ingredient of well-architected Internal Developer Platforms" 3. These paths offer pre-architected and supported approaches to building, testing, and deploying software, ensuring that developers can focus on coding rather than the intricacies of the underlying hosting infrastructure and CI/CD processes.

    One of the primary benefits of these paths is the reduction in cognitive load for developers. Kaspar von Grünberg, CEO of Humanitec, describes golden paths as procedures "in the software development life cycle that a user can follow with minimal cognitive load and that drives standardization" 4. By minimizing the mental effort required to navigate complex systems, developers can work more efficiently and effectively. These paths allow developers to spend less time dealing with CI/CD, infrastructure, and other development processes and more time on creative problem-solving, their productivity and morale are likely to improve. The 2024 State of DevOps Report notes that "self-service tooling environments and standardized automation streamline processes, making it easier for developers to achieve their goals without unnecessary delays" 2. This reduction in friction and the ability to work more efficiently contribute to a healthier work environment, ultimately helping to prevent burnout and improve overall job satisfaction.

    Golden paths are not just about making things easier; they are about making things better. They provide a route toward alignment and standardization without forcing developers into a rigid framework. As noted in a blog post by Octopus, "Instead of forcing developers to do things a certain way, you design the internal developer platform to attract developers by reducing their burden and removing pain points" 5. This approach fosters a more productive and innovative environment.

    The focus on long-term benefits is another critical aspect of golden paths. According to Spazzoli, "The more sophisticated Golden Paths are, the more they will be adopted, providing, as a result, more uniformity of configuration and behavior across the application portfolio" 3. This uniformity not only improves the quality of the software but also makes it easier to maintain and scale.

    Paved roads and golden paths are essential components of internal development platforms. They reduce cognitive load, enhance productivity, and provide long-term benefits by fostering standardization and alignment. As the software development landscape continues to evolve, these concepts will remain crucial for organizations looking to streamline their processes and support their development teams effectively.

    Conclusion

    The 2024 State of DevOps Report underscores the transformative impact of internal development platforms and the strategic importance of paved roads and golden paths. By providing standardized, self-service tooling environments, these platforms empower developers to work more efficiently and securely, fostering innovation and maintaining a competitive edge. The report highlights that "Teams do not need to sacrifice speed for stability," emphasizing that streamlined automation and integrated security are key to achieving high performance in software delivery.

    Paved roads and golden paths reduce cognitive load, enhance productivity, and ensure consistency across development processes. They offer a clear, supported route for developers, allowing them to focus on creative problem-solving rather than the complexities of CI/CD pipelines. This approach not only improves job satisfaction and prevents burnout but also drives long-term success by fostering standardization and alignment.

    In essence, the State of DevOps Report provides invaluable insights and benchmarks that help organizations enhance their DevOps capabilities. By adopting internal development platforms and leveraging paved roads and golden paths, organizations can achieve better business outcomes, support their development teams effectively, and stay ahead in the competitive tech landscape. As the software development field continues to evolve, these concepts will remain crucial for driving innovation and operational excellence.

    References

    1. 2024 State of DevOps Report: The Evolution of Platform Engineering
    2. 2024 State of DevOps Report
    3. Red Hat Blog - Designing Golden Paths
    4. Platform Engineering Blog - How to pave golden paths that actually go somewhere
    5. Octopus Blog - Paved versus golden paths in Platform Engineering
    6. Middleware Blog - Only Hard Questions: Exploring the 2024 State of DevOps Report with Lead Investigator Derek DeBellis
    7. Only Hard Questions: Exploring the 2024 State of DevOps Report with Lead Investigator Derek DeBellis

    Sunday, July 14, 2024

    Demystifying PowerShell: Method Overloading, Inheritance, and Type Casting

    PowerShell, with its object-oriented features, allows developers to create robust and flexible code. In the first article in this series, Object Oriented PowerShell, I discussed the basics of classes in PowerShell. In this post, we’ll explore a couple of code snippets that showcase method overloading, method overriding and inheritance. Our journey will take us through the intricacies of PowerShell classes and their behaviors.

    The Code

    Here we have 2 classes one derived from the other. As you can see, some of the methods in the base class are overridden in the derived class.
    class Base
    {
        [void] Foo()
        {
            Write-Host 'Base.Foo'
        }
    
        [void] Bar()
        {
            Write-Host 'Base.Bar'
        }
    
        static [void] Bar2()
        {
            Write-Host 'static Base.Bar2'
        }
    
        [void] Bar2([int] $i)
        {
            Write-Host "Bar2 int = $i"
        }
    
        # Overloaded method in the same class
        [void] Bar2([double] $f)
        {
            Write-Host "Bar2 double = $f"
        }
    }
    
    class Derived : Base
    {
        [void] Foo()
        {
            Write-Host 'Derived.Foo'
        }
    
        [void] Baz()
        {
            Write-Host 'Derived.Baz'
        }
    
        static [void] Bar2()
        {
            [Base]::Bar2()
            Write-Host 'static Derived.Bar2'
        }
    
        # Overloaded method in the derived class
        [void] Bar2([string] $s)
        {
            Write-Host "Bar2 string = $s"
        }
    }
    
    Let's now call some of the methods and see if the behavior is what we would expect. Let's start with making an instance of the base class and calling its methods. 
    # Create an instance of Base
    $base = [Base]::new()
    
    # Call the methods of Base
    $base.Foo()
    $base.Bar()
    [Base]::Bar2()
    # Here we are calling the overloaded Bar2 method
    $base.Bar2(5)
    $base.Bar2(5.3)
    
    And the results are what we would expect.
    Base.Foo Base.Bar static Base.Bar2 Bar2 int = 5 Bar2 double = 5.3
    Now let's do the same with an instance of Derived.
    # Create an instance of Derived
    $derived = [Derived]::new()
    
    # Call the methods of Base. These all behave as expected
    $derived.Foo()
    $derived.Bar()
    $derived.Baz()
    [Derived]::Bar2()
    $derived.Bar2(5)
    $derived.Bar2(5.3)
    $derived.Bar2("Hello World")
    
    And the results are also what we would expect. Ther are some things to note. The most obvious one is that we call the static method Bar2() in Base as part of the call to the overridden static method in Derived. The syntax for this is straight forward. However, this is not way to call an overridden non-static method in a base class.

    Additionally, we can add more overloads to a function in a derived class, as we did with the function Bar2 that accepts a string in Derived. 
    Derived.Foo
    Base.Bar
    Derived.Baz
    static Base.Bar2 # we correctly call the base classes Bar2 method here
    static Derived.Bar2
    Bar2 int = 5
    Bar2 double = 5.3
    Bar2 string = Hello World # This overload only existed in Derived and is called correctly here
    One other thing to note, is that you can call a method in a base class that only exists in a derived class. This provides a way to provide a base class that you are required to derive from. Here is an example of that.
    class Base
    {
        [void] Foo()
        {
            $this.Bar() # Note there is no Bar method in Base
        }
    }
    
    class Derived : Base
    {
        [void] Bar()
        {
            Write-Host "Derived.Bar"
        }
    }
    And the expected output. 
    Derived.Bar
    
    A few things to note here, as before if there is a hierarchy of classes, the method on the most derived class will be called. This example also shows how to call member methods (or access member data) it is done via '$this.'.

    Conclusion

    As you can see from this post and the last one, PowerShell provides the ability to write Object-Oriented code. There are, as noted, some limitations, such as the lack of true private methods and data.

    PowerShell’s object-oriented capabilities empower developers to build expressive code. Understanding method overloading, inheritance, and type casting is essential for creating maintainable and efficient classes.

    Feel free to ask any questions or share your thoughts! 😊

    Sunday, December 17, 2023

    Object Oriented PowerShell

    I recently discovered that PowerShell supports classes, so I started to wonder if I could code using standard Object-Oriented techniques in PowerShell. Starting with PowerShell 5.0 classes are supported. If you are going to use classes in PowerShell, I strongly suggest that you use pwsh 7.0 or above. Support there is much better than in the earlier versions.

    In this series of posts, I am going to discuss how object-oriented programming with PowerShell, what works well, and were, shall we say, there is room for improvement.

    As you probably know there are three principles of object orientation: encapsulationinheritance, and polymorphism.

    Let's start our discussion with encapsulation, the ability bundle data and methods into a single unit, typically a class.

    We can encapsulate data and methods inside of a class in PowerShell.

    class Foo {
    Foo([int] value) {
    $this.value = value
    }

    [int] GetValue()
    {
    return $this.value
    }

    hidden [int] $value
    }

    Class Foo has one data element and two methods, a constructor and a getter. One of the limitations of PowerShell is that it is not possible to hide the internal state of a class. This is typically done by determining which parts (data and methods) of the class comprise the public interface and which data and methods are internal implementation details that should not be accessed outside of the class or derived classes.

    In the case of Foo, if the intent is that the value is only settable at object construction, there is no way enforce this. 

    But, what about the keyword `hidden` before the data member, you may ask. I find the keyword hidden to be a good convention for letting the users and maintainers of the class know that the intent is to keep the method or data member, private. However, hidden data elements and members are public and therefore accessible outside of the class. The keywork hidden just hides the data member or method when you use the Get-Member cmdlet on the class.  

     In this area PowerShell is lacking. There is no way to truly make methods or data private (or protected) to the class.

    I find that when coding one of the best pieces of advice was given by Dr. Seuss in Horton Hears a Who. “Say what you mean and mean what you say”. What I mean by that is code should express the intent, and the intent expresses should be what is intended.  In the case of object-oriented languages, data and methods that should only be accessed by the class should be private, those that can be accessed by derived class should be protected, and only those that are available to everyone should be public. Unfortunately, in PowerShell there is no way to indicate something is protected and hidden is more of a suggestion than something enforceable for private data and methods.

    In PowerShell, we can encapsulate, but we cannot protect the class internals from access by outsiders. You will need to decide if this works for your project. Next up: inheritance.

    Monday, May 23, 2022

    SQL RDBMS: Don't cross the streams

    As Egon Spengler (Harold Ramis) said in Ghostbusters, "don't cross the streams", while in the SQL realm it thankfully won't cause total protonic reversal, it still is something that should be avoided.

    DDL and DML are two different types of SQL Statements that can be sent from a client to the database. DDL stands for Data Definition Language, these are the statements used to create objects in a database, tables, triggers, stored procedures, etc. DML, Data Manipulation Language, is used to manage the data in tables via select, update, insert, and delete. 

    tldr: When writing applications that use a SQL database, the SQL logins used by the applications should not execute DDL.

    Architecturally, it is best to isolate the user that is creating objects in the database from the users that executing DML queries. This follows both the principles of Least Privilege and Separation of Concerns. Unless you are working with an application that manages databases, the deployment of the app should execute DDL and the application(s) that interact with the database should not. 

    You may have noticed I said user (singular) that is creating/updating the objects in the database. As part of the deployment there should be a single user that owns all the objects in the schema/database because this will avoid broken ownership chains. Having a single owner for all objects means that only that user needs to be granted permissions to create objects in the schema/database, the other users, only need writes to access some or all of the objects in the database. 

    As with all guidelines there are a couple of caveats. Many deployments will record information about the currently installed version in a table, so the install will run DDL for this. Additionally, when running the DDL to create the objects, the deployment may need to put default data in some tables.

    Application logins may need to create temporary tables, although all else being equal, I'd rather see that in a stored procedure or function if possible. Putting that logic in a stored procedure or function creates an API for the database, which makes it easier to refactor the database without impacting the applications that use it.

    Lastly, you may have noticed that the discussion the above elides away the difference between schema and database. In general only a single schema is needed in a database. However, on rare occasions, more often in research than in business, there may be reasons to create multiple schemas in a database. This will increase complexity and should be avoided unless needed. When multiple schemas are needed, objects should still only be created as part of the deployment/ Each schema may have its own owner, or all schemas and their deployed objects may be created and owned by a single SQL User.

    Not following the above guidelines leads to unnecessary complexity that affects the maintainability of the database and the applications that use the database. Also, it often leads to unintended consequences because of that complexity.

    Happy Coding.

    Software Craftsmanship — Why Testing Matters More Than Ever

    A few weeks ago, I wrote a post called SoftwareCraftsmanship — When AI Writes the Code . The basic idea was pretty simple: AI hasn’t made so...