<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://channingwalton.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://channingwalton.github.io/" rel="alternate" type="text/html" /><updated>2026-04-15T14:32:08+00:00</updated><id>https://channingwalton.github.io/feed.xml</id><title type="html">Channing Walton</title><subtitle>Blogs and other musings</subtitle><author><name>Channing Walton</name></author><entry><title type="html">Why AI always needs a human in the loop</title><link href="https://channingwalton.github.io/posts/2026-01-30/ai-human-in-the-loop" rel="alternate" type="text/html" title="Why AI always needs a human in the loop" /><published>2026-01-30T00:00:00+00:00</published><updated>2026-01-30T00:00:00+00:00</updated><id>https://channingwalton.github.io/posts/2026-01-30/ai-human-in-the-loop</id><content type="html" xml:base="https://channingwalton.github.io/posts/2026-01-30/ai-human-in-the-loop"><![CDATA[<p>Most of the current AI tooling – LLMs, code assistants, copilots, “agents” – are astonishing at pattern-rich, feedback-dense work, but surprisingly naive everywhere else. They look like polymath geniuses when you give them well-posed, tightly constrained tasks with a clear success criterion. But put them into a real product strategy, ambiguous business requirements, socio-technical systems – and their confidence massively exceeds their competence.</p>

<p>David Epstein in his book Range, describes Kind and Wicked domains, his core claim being that in wicked domains, people with broad, cross-domain experience develop meta-skills that make them better at solving novel, messy problems than narrow specialists who typically focus on kind domains with very clear rules or constraints, like chess.</p>

<h2 id="kind-environments-where-ai-shines">Kind environments: where AI shines</h2>

<p>It’s tempting to describe LLMs as “generalists” because they can talk about pretty much anything, in any tone, with believable fluency. But under the hood, they’re closer to hyper-specialised chess prodigies. They are optimised to continue patterns that have been richly reinforced in training, not to reason from first principles in unfamiliar terrain.</p>

<p>When you give them a kind problem, they shine. Kind problems look like this:</p>

<ul>
  <li>The goal is clear: “Make these tests pass”, “Rewrite this idiomatically”, “Summarise this paper”.</li>
  <li>The constraints are explicit: input and output formats, APIs, performance budgets.</li>
  <li>Feedback is immediate and accurate: the code compiles or it doesn’t; the tests are green or red; the SQL query returns the right rows or it fails.</li>
</ul>

<p>In other words, you can wrap the task in a little pocket universe with crisp rules and fast feedback. That’s exactly what chess and golf look like for human learning. Rules never change; the board or the course is stable; the scoring function is brutally clear. In that setting, repetition and refinement dominate. The more patterns you’ve seen, the better you get.</p>

<p>AI tools are already comfortable in these pockets. They’re extremely good at:</p>

<ul>
  <li>Local code transformations with tests in place.</li>
  <li>Boilerplate architecture where the patterns are standard and “industry best practice”.</li>
  <li>Producing plausible alternatives when the scoring function is easy to automate (benchmark performance, static analysis, formal specs, etc.).</li>
</ul>

<p>If you live entirely inside these kind bubbles, AI looks like magic. Which is exactly why so many people have concluded that AI is magic.</p>

<h2 id="wicked-environments-where-the-plot-falls-apart">Wicked environments: where the plot falls apart</h2>

<p>Unfortunately, most of the work that actually matters isn’t kind.</p>

<p>A wicked environment is one where:</p>

<ul>
  <li>The problem is underspecified or contested (“What should this product be?” rather than “What should this function do?”).</li>
  <li>Feedback is slow, noisy, or confounded (did signups drop because of the UX change, the new pricing, the competitor’s launch, or the economy?).</li>
  <li>Stakeholders and constraints are in tension (regulation, culture, politics, incentives).</li>
</ul>

<p>Software is full of this. “Should we rewrite the legacy system?” is a wicked question. “What architecture should we choose?” is wicked. Even apparently simple questions like “Should we make this API public?” live in a tangle of future evolution, governance, organisational competence, and business model.</p>

<p>In these environments, simply being very good at extrapolating patterns from the past is dangerous. You’re optimising on a landscape you don’t fully understand. The data you’re learning from often encodes failures, local optima, and context-specific compromises. The patterns that “worked” weren’t necessarily good ideas; they were just the ones that didn’t visibly explode.</p>

<p>LLMs don’t know any of this. They don’t know which patterns were successes, which were accidents, and which were disasters. They don’t know which parts of the context have shifted. They don’t know that the people who wrote that confidently-worded microservices manifesto never survived the maintenance phase.</p>

<p>So in wicked domains, you get a very particular kind of failure: beautifully articulated nonsense that faithfully reproduces the average of past nonsense.</p>

<h2 id="humans-as-environment-shapers-not-just-prompt-writers">Humans as environment shapers, not just prompt writers</h2>

<p>This is where Epstein’s generalist comes back into the picture, you know, human beings.</p>

<p>The valuable human in the loop is not just the person who writes better prompts. It’s the person who can look at a messy, wicked problem and reshape it into a set of kind problems. They don’t ask the AI “What should my company strategy be?” and hope for wisdom. They do the slow, cognitively expensive job of:</p>

<ul>
  <li>Clarifying goals, trade-offs, and constraints.</li>
  <li>Identifying which parts of the problem can be made kind – i.e. made explicit, testable, and instrumented.</li>
  <li>Designing representations, interfaces, and feedback loops that allow an AI to be useful without pretending it’s omniscient.</li>
</ul>

<p>In other words, they do the thing generalists are good at: reframing.</p>

<p>Consider a realistic software scenario:</p>

<p>The wicked question is “Should we re-platform this monolith to services, and if so how?” There is no right answer in the abstract. It depends on your team, your domain, your operational maturity, your regulatory constraints, your time horizon. No amount of GitHub-trained pattern-matching will know those things.</p>

<p>But that wicked question can be decomposed into a series of kind(-ish) questions:</p>

<ul>
  <li>“Given this codebase, identify bounded contexts and candidate seams.”</li>
  <li>“Generate draft ADRs for three plausible target architectures, given these explicit constraints.”</li>
  <li>“For this specific module, suggest a migration plan to a separate service, with tests that ensure behaviour parity.”</li>
</ul>

<p>Each of those is still non-trivial, but they’re recognisably closer to the kind end of the spectrum. You can write definitions of done. You can build automated checks. You can look at outputs and say, “This is obviously wrong” without having to wait two years for production metrics.</p>

<p>The human’s job is to connect the wicked outer loop to the kind inner loops. Set direction and constraints in the messy space; let the AI churn through options and implementation details in the tidy space.</p>

<h2 id="the-risk-mistaking-fluency-for-judgement">The risk: mistaking fluency for judgement</h2>

<p>The danger, and we’re already seeing it in the wild, is when organisations invert that relationship.</p>

<p>They treat AI as if it possessed judgement in the wicked domain (“What’s our strategy?”, “How should we reorganise?”, “Which features should we build?”) and relegate humans to the implementation details that are actually kinder (“Just do what the AI spec says”). You end up with a system where the least capable agent is responsible for the hardest part of the work.</p>

<p>This is the Dunning–Kruger effect with a GPU budget: the system with the least understanding expresses the most confidence, and everyone else is subtly nudged to defer to it.</p>

<p><a href="/posts/20250-10-26/ai-dunning-kruger">AI is a Dunning-Kruger Amplifier</a>.</p>

<p>Generalist humans have seen enough domains to recognise when the problem itself is wrong, when a metric is misaligned, when a “best practice” is cargo cult, when an analogy doesn’t really hold. They know when not to trust the pattern.</p>

<h2 id="generalist-humans-specialist-machines">Generalist humans, specialist machines</h2>

<p>So where does that leave us?</p>

<p>AI systems are, and will probably remain for a while, extraordinarily powerful narrow specialists in the sense that matters here: they are tuned to excel in artificially kind micro-environments we create for them. If we bring them unfiltered wickedness, we’re going to get confident gibberish. If we bring them carefully designed kind problems, we can offload a huge amount of tedious cognitive labour.</p>

<p>The productive pattern is:</p>

<ul>
  <li>Generalist humans own problem finding and framing.</li>
  <li>Generalist humans decide what to measure and how to get feedback.</li>
  <li>Generalist humans carve out kind, checkable tasks.</li>
  <li>Specialist machines execute those tasks at scale and speed.</li>
</ul>

<p>If we get that division wrong, we’ll spend the next few years doing what over-specialised experts do in Epstein’s stories: getting better and better at winning the wrong game.</p>

<p>If we get it right, we end up with something more interesting: human generalists using AI not as an oracle, but as a force multiplier for their own meta-skills – turning wicked problems into a shape that machines can help with, without ever forgetting that the wickedness is still there, waiting outside the sandbox.</p>]]></content><author><name>Channing Walton</name></author><category term="Software" /><category term="AI" /><category term="AI" /><category term="Kind-and-Wicked" /><category term="Software-Engineering" /><summary type="html"><![CDATA[AI tools excel in kind domains with clear rules and fast feedback, but struggle in wicked domains with ambiguous requirements. Human generalists remain essential for problem framing.]]></summary></entry><entry><title type="html">Embracing Imposter Syndrome is a Superpower</title><link href="https://channingwalton.github.io/posts/2025-08-04/embracing-imposter-syndrome" rel="alternate" type="text/html" title="Embracing Imposter Syndrome is a Superpower" /><published>2025-08-04T00:00:00+00:00</published><updated>2025-08-04T00:00:00+00:00</updated><id>https://channingwalton.github.io/posts/2025-08-04/embracing-imposter-syndrome</id><content type="html" xml:base="https://channingwalton.github.io/posts/2025-08-04/embracing-imposter-syndrome"><![CDATA[<p>I spent most of the early part of my career feeling like I didn’t quite belong, standing in meetings wondering if everyone else can see that I’m just making it up as I go along. Staring at code I wrote yesterday thinking “Is this good enough?”</p>

<p>For years I thought this was a problem to solve. I was wrong.</p>

<h2 id="what-is-imposter-syndrome">What is Imposter Syndrome?</h2>

<p>Imposter syndrome is that nagging feeling that you’re not as competent as others perceive you to be. It’s the voice in your head saying you’ve fooled everyone into thinking you know what you’re doing, and any moment now they’re going to figure it out.</p>

<p>You’re a fraud! You don’t belong!</p>

<p>Most good developers I know experience this. We’re in a field that moves fast, where there’s always something new to learn, and where even experienced programmers regularly encounter problems that make them feel completely lost.</p>

<p>The conventional wisdom is that imposter syndrome is something to overcome, to push through, to defeat. But I think that’s the wrong way to go about it.</p>

<h2 id="embracing-imposter-syndrome-changes-everything">Embracing Imposter Syndrome Changes Everything</h2>

<p>Before I go on I want to be very clear: imposter syndrome itself is not a superpower - the anxiety and self-doubt are not pleasant. But embracing it, accepting it as part of who you are, is liberating and makes you better at what you do.</p>

<h3 id="it-makes-you-think-deeply">It Makes You Think Deeply</h3>

<p>When you feel like you might not know what you’re doing, you actually think about what you’re doing. You don’t coast on assumptions. You question your approach. You consider alternatives.</p>

<p>I’ve watched developers who never doubt themselves charge ahead with solutions that seemed obvious to them, only to discover later they’d missed something fundamental.</p>

<p>Meanwhile, those of us wrestling with imposter syndrome are turning problems over in our minds, looking at them from different angles, making sure we understand. But rather than being paralysed by fear of doing something wrong, we know its not perfect but together with the teams help in reviews and open discussion, it will be good enough.</p>

<h3 id="you-reflect-on-your-work">You Reflect on Your Work</h3>

<p>Embracing imposter syndrome means you’re constantly reflecting. After every project, every decision, every line of code, there’s this natural tendency to ask: “Could I have done this better? What did I miss? What would I do differently next time?”</p>

<p>This isn’t paralysis, it’s the foundation of learning and improvement.</p>

<h3 id="you-ask-questions">You Ask Questions</h3>

<p>Here’s something absolutely critical I’ve discovered: when you freely admit you don’t understand something, magical things happen.</p>

<p>First, you’ll often find that others don’t understand it either, and they’ll thank you for being brave enough to ask.</p>

<p>Second, when people do understand, they’re happy to explain it to you. People like to share what they know. But very often, during that conversation you’ll collectively realise new angles of the problem that hadn’t been considered, which is better for everyone. People will enjoy the discussion and the improved understanding.</p>

<h3 id="you-plan-for-failure">You Plan for Failure</h3>

<p>In software development, embracing imposter syndrome means you assume your code isn’t perfect, and since nobody can write perfect code, recognising this is incredibly powerful.</p>

<p>You write tests, add logging, think about error handling, and so on. You design systems that can recover gracefully when things go wrong. You build in monitoring and alerts.</p>

<p>When you accept that you’re figuring it out as you go along, you become genuinely good at figuring things out.</p>

<p>Developers who are convinced their code is bulletproof rarely do these things well. They don’t plan for failure because they don’t believe they’ll fail, and they ultimately do fail badly. Certainty is the <a href="https://youtu.be/nZXbxDJ45yA?si=Xs-3c0o1Msd1Yt5E">greatest sin</a> after all.</p>

<h3 id="you-find-good-company">You Find Good Company</h3>

<p>All the best developers I know have imposter syndrome <em>and they embrace it with enthusiasm and joy</em>. They’re the ones who are constantly learning, constantly questioning, constantly improving.</p>

<p>They’re also the ones who are genuinely excited when they discover they were wrong about something because it means they just learned something new and can improve the system.</p>

<h3 id="anxiety-decreases">Anxiety Decreases</h3>

<p>This might seem counter-intuitive, but embracing imposter syndrome reduces anxiety when you accept that you don’t have to know everything, stop trying to pretend you do, and happily ask for help. You’ll be much happier working as part of a team instead of trying to be the lone cowboy who has all the answers.</p>

<p>There’s tremendous relief in admitting you’re not perfect. It’s exhausting to maintain a facade of complete competence.</p>

<h3 id="you-become-a-better-mentor">You Become a Better Mentor</h3>

<p>When you embrace imposter syndrome, you become a better teacher and mentor.</p>

<p>You share your knowledge without arrogance because you know how much you still don’t know.</p>

<p>You don’t claim your way is the only way because you’ve been wrong before and you’ll be wrong again.</p>

<p>You teach that failure is part of the game.</p>

<p>You show that asking for help yields better outcomes.</p>

<p>You demonstrate that the best developers aren’t the ones who claim to never struggle.</p>

<p>You are kind and create a safe space for those you’re mentoring, because you’ve been there.</p>

<h3 id="the-infectious-paradox">The Infectious Paradox</h3>

<p>The moment you stop trying to prove you belong, you actually do belong. When you embrace not knowing everything and openly admit it, you become someone worth listening to.</p>

<p>You create a safe space for the people around you, and they start to feel confident to raise their questions and doubts. I have seen teams utterly transformed by this.</p>

<p>Go forth and embrace your imposter syndrome.</p>]]></content><author><name>Channing Walton</name></author><category term="programming" /><category term="learning" /><category term="psychology" /><category term="programming" /><category term="learning" /><category term="psychology" /><summary type="html"><![CDATA[Dealing with kind and wicked learning environments in software]]></summary></entry><entry><title type="html">AI is a Dunning–Kruger Amplifier</title><link href="https://channingwalton.github.io/posts/20250-10-26/ai-dunning-kruger" rel="alternate" type="text/html" title="AI is a Dunning–Kruger Amplifier" /><published>2025-08-04T00:00:00+00:00</published><updated>2025-08-04T00:00:00+00:00</updated><id>https://channingwalton.github.io/posts/20250-10-26/ai-dunning-kruger</id><content type="html" xml:base="https://channingwalton.github.io/posts/20250-10-26/ai-dunning-kruger"><![CDATA[<p>A disconnect exists between the most experienced software developers and many others, those who confidently assert that artificial intelligence can solve everything more efficiently, more creatively, and faster than mere humans. But the most competent developers (and designers, and makers in general) are quietly grimacing. We know that AI is nowhere near up to the job that the hype will have you believe. We are watching a car crash in slow motion.</p>

<p>David Dunning and Justin Kruger described a meta-cognitive blind spot where people with low ability in a domain overestimate their competence, while experts tend to underestimate theirs – the Dunning-Kruger Effect. Those who lack the skills necessary to recognise their own mistakes become overconfident, while those who truly understand a domain are acutely aware of its complexity.</p>

<p>In the AI era, the Dunning–Kruger effect has a new stage. People furthest from the realities of software design—senior managers, consultants, or people who “used ChatGPT once and were impressed”—display the highest confidence that AI can replace deep technical work.</p>

<p>But trivialising another person’s competence is a symptom of the Dunning–Kruger Effect. When highly experienced developers caution that AI’s results can look correct while being dangerously wrong, they’re not being resistant to change—they’re acting from experience. The irony is that overconfident AI advocates often interpret expert restraint as a lack of imagination. In reality, it’s quite the opposite: experts see the hidden complexity that others cannot.</p>

<p>Ironically, experienced developers can make good use of AI as a productivity amplifier, an assistant that helps with boilerplate, exploration, or prototyping. But they do so with discernment and scepticism, because they know the boundaries between useful automation and untrustworthy suggestions. They are still in control, still scrutinising the code carefully.</p>

<p>When experienced engineers use AI, they can achieve real acceleration — but that acceleration depends on foundational knowledge. They know that clean, safe, efficient software isn’t just about producing an answer that works once. It’s about maintainability, test coverage, edge cases, and correctness under changing conditions. AI can generate code that looks elegant but fails all of those tests. It’s easy to make software <em>appear</em> right while being dangerously wrong — that’s why experience remains non-negotiable.</p>

<p>The paradox is that AI is only effective and safe in the hands of competent, experienced people.</p>

<p>AI itself can also suffer from the Dunning–Kruger Effect. Every developer who has ever used Claude Code will attest to its ridiculous overconfidence even when nothing works at all! Studies such as <a href="https://arxiv.org/html/2510.05457v1">Do Code Models Suffer from the Dunning–Kruger Effect?</a> and <a href="https://arxiv.org/abs/2505.02151">Large Language Models Are Overconfident</a> show models that systematically overestimate their accuracy — particularly when their actual performance is lowest. AI mirrors the same psychological bias as inexperienced, overconfident people.</p>

<p>In effect, AI doesn’t just reflect Dunning–Kruger; it <em>amplifies</em> it. The result is a “force multiplier” effect for misplaced confidence, especially when decision-makers lack the grounding to know when the system is wrong.</p>

<p>It is more important than ever to temper the wild promises of AI with the experience of practitioners.</p>]]></content><author><name>Channing Walton</name></author><category term="AI" /><category term="programming" /><category term="psychology" /><category term="AI" /><category term="programming" /><category term="psychology" /><summary type="html"><![CDATA[AI doesn't just reflect Dunning–Kruger; it amplifies it]]></summary></entry><entry><title type="html">Kind and Wicked</title><link href="https://channingwalton.github.io/posts/2024-10-19/kind-and-wicked" rel="alternate" type="text/html" title="Kind and Wicked" /><published>2024-10-19T00:00:00+00:00</published><updated>2024-10-19T00:00:00+00:00</updated><id>https://channingwalton.github.io/posts/2024-10-19/kind-and-wicked</id><content type="html" xml:base="https://channingwalton.github.io/posts/2024-10-19/kind-and-wicked"><![CDATA[<p>The psychologist Robin Hogarth introduced the concept of “kind” and “wicked” learning environments to explain why experience doesn’t always lead to improved performance.</p>

<p>Kind learning environments are those where the rules are clear and feedback is immediate and accurate, such as board games. Wicked learning environments are the opposite with unclear rules, delayed or uncertain feedback, and is common in real-world, complex systems involving human beings. A kind environment doesn’t mean an easy environment, just that the means of establishing progress and measuring mastery are clear.</p>

<p>Computer Science is a kind environment: the syntax of programming languages is well-defined, errors are immediately highlighted by compilers or interpreters, and feedback is clear and precise. Many algorithms such as sorting or searching are also kind problems with clear objectives and outcomes. Computer scientists can verify the correctness of algorithms mathematically, or through test cases, receiving straightforward feedback that guides learning and improvement.</p>

<p>As software developers, we make our environments kinder with tools and techniques, although sometimes we make it a lot worse but that is a different story. An example is unit testing, which helps to create a kinder environment by setting clear expectations for each unit of code and provides fast, immediate, and accurate feedback about the code under test.</p>

<p>However, designing large-scale systems is very often a wicked environment in more ways than one. Goals and requirements are unclear, frequently change, and are full of contradictions as many interested groups bring their particular needs and perspectives to the system. The work of engineers and analysts is to gather that mess of requirements, ideas, constraints, and produce something coherent in a continuously deliverable way, that is capable of evolving and growing with changing needs: <em>Building the plane while flying</em>.</p>

<p>The trick, the art, is to turn those wicked problems into a stream of coherent, kind problems that can be built and delivered thousands of times over many years, delivering value, without being crushed under the weight of previous mistakes <em>that will be made</em>.</p>

<p>To my mind, this is a central difference between (computer) science and (software) engineering - an argument for another day perhaps.</p>

<p>In many projects I’ve worked on, when I finally sit down to write code there is a sense of relief that finally I can do something concrete that has a clear direction and outcome. But that only happens when sufficient analysis and planning has been done well, when all the wicked problems have been tamed and a set of kind problems has been left. If developers sit down to code without that sense of clarity, they should stop and reconsider what they’re about to do.</p>

<p>Bringing this back to learning, the problem with these wicked projects is that they are wicked learning environments too. After years of working in software it often feels as though you have learned a huge amount and nothing at all, or at least nothing that can be distilled and taught succinctly. It is also easy to confuse the depth of learning achieved on the kind side of programming with having any depth of understanding of the wicked side.</p>

<p>Conferences, books, blogs, and videos, are full of great, important, and necessary, material that largely address kind problems: testing, programming techniques, new languages and paradigms. But we very rarely hear solutions to the really difficult, wicked problems, which is understandable because its almost impossible to quantify or distil those problems to something generally applicable.</p>

<p>In one of the first software projects I worked on there were a couple of older developers on the team that I found frustrating at times because they seemed ponderous when I wanted to get to the code because <em>I knew the way forward</em>.</p>

<p>I quickly learned that when one of them went quiet, frowned, and said, “Hang on a minute youngster”, ripping the keyboard from under my fingers, I should stop and listen. What they said involved gut instincts and feelings that weren’t concrete, they struggled to pinpoint exactly what was bothering them, but <em>there be dragons here</em> and they were invariably right. They saw problems and eventually solutions I couldn’t yet.</p>

<p>Often, what they were seeing were analogies with other systems in totally unrelated domains that they had worked on, and had <em>simpler solutions</em> to the ones we were blundering in to.</p>

<p>Experience really matters in the wicked world, but, as Hogarth found, not always. However, it is all we have!</p>

<p>If you have read this far and are hoping for a punchline, a brilliant solution to tackling these wicked problems … sorry, I don’t have one because … well, see above.</p>

<p>But I can say the following:</p>

<p>Look out for the wicked problems, you’ll know them by the behaviours they produce: analysis paralysis, confusion, arguments, people shifting uncomfortably in their seats and warning of dragons, developers bringing up lots of issues.</p>

<p>When all is said and done, the place where ideas become reality is in the building of a thing where problems materialise. Dismiss or diminish these issues as technical concerns at your peril, and even if they were technical issues that is still a business issue.</p>

<p>Look out for teams and people that seem to consistently deliver and learn from them, I call them <a href="/posts/2015-09-15/what#silent-running">silent runners</a>. They’re the ones drowning in a sea of green at the bottom of your RAG reports, and people often say, “Oh, their problems are simple” … really?</p>

<p>Finally, the real question we need to ask ourselves as an industry is how can we learn from the wicked environment we find ourselves in, and teach what we can to the next generation of engineers?</p>]]></content><author><name>Channing Walton</name></author><category term="programming" /><category term="learning" /><category term="programming" /><category term="learning" /><summary type="html"><![CDATA[Dealing with kind and wicked learning environments in software]]></summary></entry><entry><title type="html">Rumblings in Scala</title><link href="https://channingwalton.github.io/posts/2023-05-02/scala" rel="alternate" type="text/html" title="Rumblings in Scala" /><published>2023-05-02T00:00:00+00:00</published><updated>2023-05-02T00:00:00+00:00</updated><id>https://channingwalton.github.io/posts/2023-05-02/scala</id><content type="html" xml:base="https://channingwalton.github.io/posts/2023-05-02/scala"><![CDATA[<p>Here are some thoughts I posted about Scala on <a href="https://types.pl/@channingwalton/110299339793580597">Mastodon</a>.</p>

<p>There is quite a bit of talk about Scala and it’s future it seems.
There are thoughts about academia driving the language for its own noble goals but losing sight of the consequences for industrial size codebases in the wild and the ecosystem as a whole.</p>

<p>It’s a justifiable opinion. For example, the IDE support has been a long struggle and is pretty good for 2, but it has taken a big step backwards for 3. It’s become a tiring and unnecessary impediment to just getting things done for the average programmer who doesn’t want to repeatedly quit the IDE, killall java, or reimport projects because “something weird has happened”. We don’t have time for that crap.</p>

<p>Now you might all say I don’t know what I’m talking about, that with some clever solutions to X and Y and using a proper editor, everything will be great. Maybe, but having worked hard at trying a lot of these tools and IDEs, I’ve found little joy with Scala 3.  I’ve also tried migrating systems to Scala 3 several times and gave up because it is a herculean task with considerable risk to the systems.</p>

<p>I’m not a type astronaut or contributor to libraries like the type level ecosystem – I’m not smart enough. But, I think I can say I’m not bad at building systems by leveraging the work of those geniuses. And, I am pretty happy working with Scala 2 and enjoying the improvements around the ecosystem that are happening all the time.</p>

<p>I’m enormously grateful to all those really smart people working very hard, for free, to make it possible for me to have an enjoyable career.</p>

<p>The success of the language depends critically on an ecosystem sustained by very smart people giving up their free time, and so it is imperative that changes to the language do not exhaust their good will and patience. I fear that Scala 3, whilst having many great features, was too much too fast for everyone.</p>]]></content><author><name>Channing Walton</name></author><category term="programming" /><category term="scala" /><category term="programming" /><category term="scala" /><summary type="html"><![CDATA[There is some unhappiness with the state of Scala]]></summary></entry><entry><title type="html">Hiring a programmer? Make some tea!</title><link href="https://channingwalton.github.io/posts/2023-04-24/jobs" rel="alternate" type="text/html" title="Hiring a programmer? Make some tea!" /><published>2023-04-24T00:00:00+00:00</published><updated>2023-04-24T00:00:00+00:00</updated><id>https://channingwalton.github.io/posts/2023-04-24/jobs</id><content type="html" xml:base="https://channingwalton.github.io/posts/2023-04-24/jobs"><![CDATA[<p>Whilst looking for my next job, I’m finding that the hiring process has become little more than an exercise in matching tags.</p>

<p>A few years ago I responded to a question on Quora, <a href="https://qr.ae/pyRRiu">How do I explain to non-programmers how complex, time-consuming, and error-prone software development is?</a>.</p>

<p>The essence of my response was to describe how an apparently simple process of making tea is not as simple as it first seems, and that the thinking behind it contains thousands of details that programmers
need to consider.</p>

<p>It was a popular response that resonated with a lot of developers.</p>

<p>A few days ago I <a href="https://twitter.com/channingwalton/status/1650133214126645254">tweeted</a> a joke about how we’d hire drivers like we hire programmers. But I am going to keep the tea analogy going and recast it:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>If we advertised for someone to make tea like devs.

Required:
- three years of brewing experience
- can boil a kettle
- knowledge of cold water and electricity, particular mains AC
- experience with Yorkshire tea bags, PG tips. Tetley a bonus
- proven and demonstrateable skills chopping tea leaves
- chemistry of oxidation converting polyphenols into
  compounds such as theaflavins and thearubigins
- Bone china cup making 
</code></pre></div></div>

<p>For developers, a lot of job adverts read that way.</p>

<p>A common thing that comes up is whether the candidate has previously used a particular product like a database or queue. Those tools are big and complicated, and appear in architecture diagrams and are rightfully critical to the system. So, it is natural to ask if the candidate has used them before.</p>

<p>But here’s the thing, from a programmers perspective <em>the specific products are not a big deal</em>. The difference from one database to the next is trivial and has very little impact on what the programmer is doing day to day.</p>

<p>If those systems have a large impact daily, then you have a much bigger problem, your architecture or use of those products is inappropriate, and a good developer will debug it.</p>

<p>Some products have novel features that others don’t, and any developer will learn those features quickly (hours), and apply them to the problem at hand.</p>

<p>It feels like an easy shortcut to match candidates against products and techniques (TDD, ATDD, BDD, OO, FP, etc.), but by doing so you’re missing out on good people which will cost you more in the long run.</p>

<p>Focus on what really matters. Is the candidate passionate about solving problems, building what users need, striving for simplicity, producing bug free systems, and know how to do those things?</p>

<p>Most importantly, can the team have an enjoyable cup of tea with the candidate whilst discussing how the system and processes you’re building could be improved?</p>]]></content><author><name>Channing Walton</name></author><category term="programming" /><category term="hiring" /><category term="programming" /><category term="hiring" /><summary type="html"><![CDATA[Hiring programmers is difficult, but we are making it much harder than it should be.]]></summary></entry><entry><title type="html">You want to use AI? Learn to program first!</title><link href="https://channingwalton.github.io/posts/2023-04-19/ai" rel="alternate" type="text/html" title="You want to use AI? Learn to program first!" /><published>2023-04-19T00:00:00+00:00</published><updated>2023-04-19T00:00:00+00:00</updated><id>https://channingwalton.github.io/posts/2023-04-19/ai</id><content type="html" xml:base="https://channingwalton.github.io/posts/2023-04-19/ai"><![CDATA[<p>Whilst using AI tools to help me write software, it struck me that these tools in their current form, are only safe in
the hands of experienced developers.</p>

<p>There are many examples of expert level tools that are dangerous in the hands of novices, such as power tools in
carpentry or advanced surgical instruments. Novices need to earn the right to use those tools and prove they know how to
use them safely.</p>

<p>And so it is with the current crop of AI tools which are dangerous in naive hands because they hallucinate, and produce
answers that need careful handling and scrutiny.</p>

<p>Programming has some natural protections that other industries don’t. For example, the code produced by AI often doesn’t
compile or is just clearly nonsense. But passing the bar exam is a party trick, would you take legal advice from
an AI? If you would I have a bridge to sell you, perhaps you can ask the AI to draft the details.</p>

<p>Code compilation isn’t the big issue, AI often produces plausible looking code that does compile and run. The
problem is that solutions often have subtle bugs, or worse, push designs in the wrong direction leading to expensive
time-wasting and dead ends. Remember, it doesn’t have a big picture … of anything!</p>

<p>It takes experience to see the solutions offered by these tools, find what’s useful, and apply them to a much larger and
more complex context in the design and architecture of the system being built.</p>

<p>My advice if you’re starting out in programming is to learn how to program, design and architect solutions, before
looking for shortcuts that aren’t there.</p>

<p>Embrace the grind, pay the fee, learn the craft.</p>]]></content><author><name>Channing Walton</name></author><category term="programming" /><category term="AI" /><category term="programming" /><category term="AI" /><summary type="html"><![CDATA[Learn your craft before being tempted by shortcuts that aren't there.]]></summary></entry><entry><title type="html">Introducing the Ritronome</title><link href="https://channingwalton.github.io/posts/2023-04-04/ritronome" rel="alternate" type="text/html" title="Introducing the Ritronome" /><published>2023-04-04T00:00:00+00:00</published><updated>2023-04-04T00:00:00+00:00</updated><id>https://channingwalton.github.io/posts/2023-04-04/ritronome</id><content type="html" xml:base="https://channingwalton.github.io/posts/2023-04-04/ritronome"><![CDATA[<p><em>Ritronome</em> is an experimental metronome that can change tempo.</p>

<p>Conventional metronomes are very useful for all kinds of practice but they cannot be used for music that slows down or speeds up.
So I wrote <a href="/rit">Ritronome</a> to do exactly that and find out if having a variable metronome would actually help.</p>

<p>Go and try it out and let me know what you think on <a href="https://bsky.app/profile/channingwalton.bsky.social">Bluesky</a>.</p>]]></content><author><name>Channing Walton</name></author><category term="music" /><category term="music" /><summary type="html"><![CDATA[An experimental metronome that can change tempo]]></summary></entry><entry><title type="html">Complexity Trap</title><link href="https://channingwalton.github.io/posts/2023-01-25/Complexity" rel="alternate" type="text/html" title="Complexity Trap" /><published>2023-01-25T00:00:00+00:00</published><updated>2023-01-25T00:00:00+00:00</updated><id>https://channingwalton.github.io/posts/2023-01-25/complexity-trap</id><content type="html" xml:base="https://channingwalton.github.io/posts/2023-01-25/Complexity"><![CDATA[<p>There comes a point in complex domains where only developers have anything close to a complete understanding of the system behaviour. If you’re lucky.</p>

<p>I’ve worked on projects in finance and media that had reasonably complex behaviour. They start from basic requirements such as a simple, linear, workflow for bank workers to open accounts for new clients.</p>

<p>Then, in response to reasonable changes over time, things become complex. For example, the banker might need to ask the client questions so the application form is returned.</p>

<p>Later, the process needs to support multiple account holders so each account holder must complete an application form whilst the lead applicant completes more general details. All the forms must be collated and processed by the back office.</p>

<p>Then, automated identity checks are added to the process. They are asynchronous, taking minutes, so each relevant part of the application process for each account opener needs to be managed before the whole set of applications is sent for back office processing.</p>

<p>When the back office process involves multiple teams, with the potential for parts of applications to move between teams, things become even more complex.</p>

<p>That project had about 30 different stakeholder groups across the bank interested in different aspects of the system. <em>The only group that had the whole picture was the development team</em>. Analysts, managers, back office users, and other groups were unable to understand the whole system even though, collectively, they’d asked for all of it. And thats ok.</p>

<blockquote>
  <p>Development is the crucible in which everything converges</p>
</blockquote>

<h2 id="was-it-wrong">Was it wrong?</h2>

<p>At every step in its evolution the system had features added for good reasons and it worked as intended. Everyone was happy because it automated a complex system that had been intensely manual. Accounts that took months to open were now opened in hours.</p>

<p>But, there was an uncomfortable accretion of complexity that didn’t exist with the manual process because it wasn’t feasible to manually do the things the system was doing.</p>

<p>Whilst the process was now rigorous and everything <em>could</em> be explained, users were nevertheless confused as they were seeing a small part of a much larger process.</p>

<p>It is difficult to know when complexity crosses the line where people, particularly non-devs, can comprehend everything. As developers we are able to look at code, technical docs, and tests, to help us. If users are not provided with similar tooling, which they won’t necessarily ask for, they will ask questions that developers will need to spend time answering which is not efficient for anyone.</p>

<p>We should be sensitive to increasing complexity and explore solutions to reduce it. That can be difficult with large systems built from complex requirements arising from the needs of many stakeholders, each of which may not fully appreciate the problems they’re creating for people who work directly with the system.</p>

<p>In our banking system, we ended up providing tools to help users see where applications were in the workflow and why, which was very helpful.</p>]]></content><author><name>Channing Walton</name></author><category term="software" /><category term="analysis" /><category term="design" /><category term="software" /><category term="analysis" /><category term="design" /><summary type="html"><![CDATA[An observation of how complexity can creep up on you]]></summary></entry><entry><title type="html">Functor</title><link href="https://channingwalton.github.io/posts/2022-08-29/Functors" rel="alternate" type="text/html" title="Functor" /><published>2022-08-29T00:00:00+00:00</published><updated>2022-08-29T00:00:00+00:00</updated><id>https://channingwalton.github.io/posts/2022-08-29/functor</id><content type="html" xml:base="https://channingwalton.github.io/posts/2022-08-29/Functors"><![CDATA[<p>A non-mathematical introduction to functors.</p>

<p><code class="language-plaintext highlighter-rouge">Option</code> and <code class="language-plaintext highlighter-rouge">List</code> are two well-known classes, both of which have the function <code class="language-plaintext highlighter-rouge">map</code>
that takes a function that is applied to the values contained by the <code class="language-plaintext highlighter-rouge">List</code> or <code class="language-plaintext highlighter-rouge">Option</code>.</p>

<p>Here are two functions that take <code class="language-plaintext highlighter-rouge">List[Int]</code> and <code class="language-plaintext highlighter-rouge">Option[Int]</code> and increments the values:</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">inc</span><span class="o">(</span><span class="n">o</span><span class="k">:</span> <span class="kt">Option</span><span class="o">[</span><span class="kt">Int</span><span class="o">])</span><span class="k">:</span> <span class="kt">Option</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span> <span class="k">=</span> <span class="nv">o</span><span class="o">.</span><span class="py">map</span><span class="o">(</span><span class="n">i</span> <span class="k">=&gt;</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="o">)</span>

<span class="k">def</span> <span class="nf">inc</span><span class="o">(</span><span class="n">l</span><span class="k">:</span> <span class="kt">List</span><span class="o">[</span><span class="kt">Int</span><span class="o">])</span><span class="k">:</span> <span class="kt">List</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span> <span class="k">=</span> <span class="nv">l</span><span class="o">.</span><span class="py">map</span><span class="o">(</span><span class="n">i</span> <span class="k">=&gt;</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="o">)</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">inc</code> functions do not care about <code class="language-plaintext highlighter-rouge">Option</code> or <code class="language-plaintext highlighter-rouge">List</code> particularly, they need a
way to work on the values they contain, but sadly <code class="language-plaintext highlighter-rouge">inc</code> is duplicated. It doesn’t have to be.</p>

<p>One way to fix this is to use a <code class="language-plaintext highlighter-rouge">Functor</code> that supplies a <code class="language-plaintext highlighter-rouge">map</code> function for any <code class="language-plaintext highlighter-rouge">F[_]</code>.</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// scala 3</span>
<span class="k">trait</span> <span class="nc">Functor</span><span class="o">[</span><span class="kt">F</span><span class="o">[</span><span class="k">_</span><span class="o">]]</span> <span class="o">{</span>
  <span class="n">extension</span> <span class="o">[</span><span class="kt">A</span>, <span class="kt">B</span><span class="o">](</span><span class="n">x</span><span class="k">:</span> <span class="kt">F</span><span class="o">[</span><span class="kt">A</span><span class="o">])</span>
    <span class="k">def</span> <span class="nf">map</span><span class="o">(</span><span class="n">f</span><span class="k">:</span> <span class="kt">A</span> <span class="o">=&gt;</span> <span class="n">B</span><span class="o">)</span><span class="k">:</span> <span class="kt">F</span><span class="o">[</span><span class="kt">B</span><span class="o">]</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Given <code class="language-plaintext highlighter-rouge">x: F[A]</code>, <code class="language-plaintext highlighter-rouge">Functor[F]</code> provides a <code class="language-plaintext highlighter-rouge">map</code> method. Here is <code class="language-plaintext highlighter-rouge">inc</code> written using <code class="language-plaintext highlighter-rouge">Functor[F]</code>:</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">inc</span><span class="o">[</span><span class="kt">F</span><span class="o">[</span><span class="k">_</span><span class="o">]</span><span class="kt">:</span> <span class="kt">Functor</span><span class="o">](</span><span class="n">a</span><span class="k">:</span> <span class="kt">F</span><span class="o">[</span><span class="kt">Int</span><span class="o">])</span><span class="k">:</span> <span class="kt">F</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span> <span class="k">=</span> <span class="nv">a</span><span class="o">.</span><span class="py">map</span><span class="o">(</span><span class="n">i</span> <span class="k">=&gt;</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="o">)</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">F[_]: Functor</code> means that it requires an <em>instance</em> of <code class="language-plaintext highlighter-rouge">Functor[F]</code>.
Scala finds that instance by looking in <a href="https://docs.scala-lang.org/scala3/reference/changed-features/implicit-resolution.html#inner-main">lots of places</a>
for it, one being the companion object of the <code class="language-plaintext highlighter-rouge">F[_]</code>.</p>

<p>For <code class="language-plaintext highlighter-rouge">Option</code> and <code class="language-plaintext highlighter-rouge">List</code> those instances are:</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">given</span> <span class="nc">Functor</span><span class="o">[</span><span class="kt">Option</span><span class="o">]</span> <span class="k">with</span> <span class="o">{</span>
  <span class="n">extension</span> <span class="o">[</span><span class="kt">A</span>, <span class="kt">B</span><span class="o">](</span><span class="n">m</span><span class="k">:</span> <span class="kt">Option</span><span class="o">[</span><span class="kt">A</span><span class="o">])</span>
    <span class="k">override</span> <span class="k">def</span> <span class="nf">map</span><span class="o">(</span><span class="n">f</span><span class="k">:</span> <span class="kt">A</span> <span class="o">=&gt;</span> <span class="n">B</span><span class="o">)</span><span class="k">:</span> <span class="kt">Option</span><span class="o">[</span><span class="kt">B</span><span class="o">]</span> <span class="k">=</span> <span class="nv">m</span><span class="o">.</span><span class="py">map</span><span class="o">(</span><span class="n">f</span><span class="o">)</span>
<span class="o">}</span>

<span class="n">given</span> <span class="nc">Functor</span><span class="o">[</span><span class="kt">List</span><span class="o">]</span> <span class="k">with</span> <span class="o">{</span>
  <span class="n">extension</span> <span class="o">[</span><span class="kt">A</span>, <span class="kt">B</span><span class="o">](</span><span class="n">m</span><span class="k">:</span> <span class="kt">List</span><span class="o">[</span><span class="kt">A</span><span class="o">])</span>
    <span class="k">override</span> <span class="k">def</span> <span class="nf">map</span><span class="o">(</span><span class="n">f</span><span class="k">:</span> <span class="kt">A</span> <span class="o">=&gt;</span> <span class="n">B</span><span class="o">)</span><span class="k">:</span> <span class="kt">List</span><span class="o">[</span><span class="kt">B</span><span class="o">]</span> <span class="k">=</span> <span class="nv">m</span><span class="o">.</span><span class="py">map</span><span class="o">(</span><span class="n">f</span><span class="o">)</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Your own classes can play too:</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">final</span> <span class="k">case</span> <span class="k">class</span> <span class="nc">Blub</span><span class="o">[</span><span class="kt">A</span><span class="o">](</span><span class="n">v</span><span class="k">:</span> <span class="kt">A</span><span class="o">)</span>

<span class="k">object</span> <span class="nc">Blub</span> <span class="o">{</span>
  <span class="n">given</span> <span class="nc">Functor</span><span class="o">[</span><span class="kt">Blub</span><span class="o">]</span> <span class="k">with</span> <span class="o">{</span>
    <span class="n">extension</span> <span class="o">[</span><span class="kt">A</span>, <span class="kt">B</span><span class="o">](</span><span class="n">blub</span><span class="k">:</span> <span class="kt">Blub</span><span class="o">[</span><span class="kt">A</span><span class="o">])</span>
      <span class="k">override</span> <span class="k">def</span> <span class="nf">map</span><span class="o">(</span><span class="n">f</span><span class="k">:</span> <span class="kt">A</span> <span class="o">=&gt;</span> <span class="n">B</span><span class="o">)</span><span class="k">:</span> <span class="kt">Blub</span><span class="o">[</span><span class="kt">B</span><span class="o">]</span>
        <span class="k">=</span> <span class="nc">Blub</span><span class="o">(</span><span class="nf">f</span><span class="o">(</span><span class="nv">blub</span><span class="o">.</span><span class="py">v</span><span class="o">))</span>
  <span class="o">}</span>
<span class="o">}</span>

<span class="nf">inc</span><span class="o">(</span><span class="nc">Blub</span><span class="o">(</span><span class="mi">1</span><span class="o">))</span> <span class="n">gives</span> <span class="nc">Blub</span><span class="o">(</span><span class="mi">2</span><span class="o">)</span>
</code></pre></div></div>

<p>Note that this is an example of the typeclass pattern, read more <a href="https://docs.scala-lang.org/scala3/book/types-type-classes.html">here</a>.</p>

<h2 id="laws">Laws</h2>

<p>A proper functor must obey two laws:</p>

<ol>
  <li><em>Identity</em>: Mapping with the <em>identity</em> function is a no-op</li>
  <li><em>Composition</em>: <code class="language-plaintext highlighter-rouge">fa.map(f).map(g) = fa.map(f.andThen(g)</code></li>
</ol>

<h2 id="read-more">Read more</h2>

<ul>
  <li><a href="https://typelevel.org/cats/typeclasses/functor.html">TypeLevel</a></li>
</ul>]]></content><author><name>Channing Walton</name></author><category term="software" /><category term="scala" /><category term="software" /><category term="scala" /><summary type="html"><![CDATA[A non-mathematical introduction to functors]]></summary></entry></feed>