The Permission to Try
Published:
Yesterday, I was in one of those conversations that starts as a catch-up between engineers and ends as something more like a collective working-out of what the industry is becoming. The kind of conversation where someone says something and the rest of the group goes quiet for a moment before someone else says “yes, that.”
The trigger, for me at least, was a LinkedIn post from a startup founder I’d worked with a few years back, someone whose technical instincts I’ve consistently found to be ahead of the curve. He was writing about a shift he’d noticed in how his team was working: specifically, that they were trying more things. Not shipping more, but trying more. Moving faster through exploration before committing to approaches. And that AI tooling was the reason.
What followed, between those of us on the call, was a recognition that we’d all been observing the same pattern independently. Engineers we respect, teams we work with, our own practice. The calculus of what’s worth attempting has changed. Not dramatically, not overnight, but persistently. And the reason it took us a while to articulate it clearly is that it runs against something quite deep in how our profession has trained us to think.
The 50-Year Discipline of No
If you’ve been in software long enough, you know the feeling. Someone proposes a feature, an integration, a service, a rewrite. And somewhere in your gut, before the technical analysis even begins, is a kind of reflexive caution. An instinct toward not.
That instinct wasn’t accidental. It was cultivated.
Over the past fifty years, the craft of software engineering has been largely defined by the effort to reduce the amount of code you need to write to solve a problem. Think about what that arc looks like.
The structured programming movement of the 1970s gave us subroutines and modules: stop writing the same instructions repeatedly, stop following execution through spaghetti control flows. The object-oriented wave of the 1980s and 90s gave us reusable components, encapsulation, inheritance: model the problem domain in a way that lets concepts compose rather than copy.
Design patterns emerged in the mid-90s as a shared vocabulary for solutions that had already been figured out: don’t reinvent the wheel, learn to recognise the shape of it.
A set of principles crystallised around this goal:
- DRY (Don’t Repeat Yourself): every piece of knowledge should have a single, authoritative representation.
- YAGNI (You Aren’t Gonna Need It): don’t write code for requirements that don’t exist yet.
- SOLID: a framework for object-oriented design that favoured extensibility without modification, keeping future complexity manageable.
- Frameworks and libraries: someone has already solved the authentication problem, the ORM problem, the HTTP routing problem. Use their solution; don’t write your own.
This discipline didn’t stay at the level of individual code decisions. It scaled upward through every layer of the organisation.
At the architectural level, it became the preference for proven infrastructure over hand-rolled solutions, and for boring technology over new and fashionable technology. The relational database that everyone knows how to operate, debug, and scale is almost always preferable to the novel document store that promises to solve problems you may not have. Mature technology has well-understood failure modes, established tooling, and a large pool of engineers who can reason about it. A new technology carries hidden costs: unknown failure modes, shallow community knowledge, and the risk that the thing you’ve built your system around turns out to have a fundamental limitation you only discover in production.
At the business level, the same instinct became the CTO’s preference for off-the-shelf products over custom builds for anything that wasn’t core to competitive differentiation. Why build a billing system when Stripe exists? Why build a CRM when Salesforce does? Every line of custom code is a line someone has to own, and software you didn’t write is software you don’t have to maintain. The principle that drove DRY and YAGNI at the code level drove “boring over shiny” at the architecture level and “buy vs build” at the strategy level. It was the same discipline, applied at different altitudes.
The underlying logic was always about cost. Code is expensive to write, but more importantly, it is expensive to maintain. Every line of code is something that needs to be tested, debugged, refactored, understood by the next person who encounters it, and eventually either updated or replaced. The cost of code doesn’t end at the point of authorship. It continues for the lifetime of the system.
Saying “no” was how engineering teams managed that cost:
- No to the clever abstraction that would save fifty lines now but mystify anyone who encountered it later.
- No to the new dependency that would need to be kept current.
- No to the feature that wouldn’t justify its weight.
This discipline wasn’t timidity; it was rationality. Teams that didn’t cultivate it ended up buried under complexity they couldn’t navigate, commitments they couldn’t keep, and codebases that became progressively harder to change.
This is why code quality, testability, maintainability, and reusability became the canonical virtues of the craft. Not because engineers are inherently conservative, but because the alternatives had visible, documented consequences.
The Equation That Changed
Here’s what’s happened: one half of that cost equation has shifted dramatically.
The cost of writing code has plummeted. Not to zero. Not remotely to zero. You still need to understand what you’re building well enough to direct the tools. You still need to read and evaluate the output. You still need to make the architectural decisions, define the interfaces, ask the right questions. But a spike solution that might have taken three days now takes three hours. A proof-of-concept that wasn’t economically viable to attempt is now worth attempting. An integration that would have required pulling someone off their current work for a week can be drafted in an afternoon.
The cost of maintaining code, however, hasn’t changed in any meaningful way. Code you generate with AI assistance still needs to be tested. It still needs to be understood by the people who will work with it. It still needs to be refactored as requirements evolve, updated as dependencies shift, and eventually replaced when it outlives its purpose. The maintenance burden of a feature is largely independent of how quickly the initial implementation was produced.
This asymmetry is the thing the industry is still working out how to think about clearly. The temptation is to treat reduced writing cost as a licence to ship more: to say yes to more features, more complexity, more scope. But that misreads the equation. The correct reading is that reduced writing cost is a licence to explore more before you commit. To fail faster and earlier, in the phase when failure is cheap.
Agile introduced spike solutions precisely for this reason: time-boxed experiments to validate assumptions before committing to implementation. The problem with spikes in practice is that they remain expensive relative to the value of the information they generate. AI changes that ratio significantly. When a spike takes a few hours rather than a few days, you can afford to run three spikes instead of one. You can explore the problem space rather than committing to the first approach that seems credible. You can accumulate evidence before making the decision, rather than making the decision under time pressure and hoping it was right.
The discipline of “no” hasn’t disappeared. But it has moved. The question used to be asked at the start of a piece of work: should we write this? Increasingly, the question is better asked at the end of the exploration phase: should we keep this?
That’s a meaningfully different conversation.
Not Just Code: Entire Stacks
There’s a corollary to the reduced writing cost that deserves its own examination, because it extends the implication beyond individual features and into something much larger.
For the past few decades, choosing a technology stack was a weighty commitment. Not just architecturally, but in terms of the team you needed to build around it and the length of time it would take any engineer to become productive in it. Knowing Python well enough to write idiomatic, performant, maintainable Python takes time. Knowing Go, Ruby, TypeScript, Rust (not just the syntax but the idioms, the ecosystem, the conventions, the gotchas) takes time.
Knowing how a specific framework handles state management, or how a particular database’s query planner behaves under load, or how a cloud provider’s IAM model intersects with your security requirements: all of that is accumulated through exposure and error, not through reading documentation once.
This created a rational conservatism around stack decisions. You stayed with what your team knew. You were cautious about introducing new languages or frameworks because the cost of onboarding to them was significant. You thought hard before picking up a new dependency because someone would need to understand it deeply enough to maintain and eventually replace it.
What we are now seeing, confirmed through conversations with engineers I deeply respect, is that experienced engineers who understand concepts, algorithms, and patterns can transfer that knowledge across technology stacks with substantially less friction than before. The conceptual knowledge (event-driven architecture, async programming models, data structure trade-offs, distributed system failure modes, algorithmic complexity) has always been portable in principle. What wasn’t portable was the surface: the syntax, the library names, the idiomatic patterns, the build toolchain.
AI fills exactly that gap. An engineer who has built event-driven systems in Node can now work meaningfully in a Go codebase without months of immersion. An engineer with deep experience in relational database design can contribute to a service written in a language they’ve never used commercially, because the concepts they carry are the hard part and the AI handles the translation.
This is not without limits, and it’s important to be honest about them. AI fills the gap at the surface, but stack-specific depth (performance characteristics under specific load patterns, subtle security considerations, community conventions around error handling or testing) still requires judgment and, often, experience. The engineer who uses AI to bridge into an unfamiliar stack can contribute meaningfully much sooner, but they are not the same as an engineer who has spent years in that stack. The gap narrows dramatically. It does not disappear.
What changes is the economics of exploration. You can now afford to ask: what would this look like in a different stack? And get a credible answer quickly enough that it informs a real decision, rather than remaining a hypothetical.
The Case for Saying Yes
The logical consequence of all of this is a shift in how experienced engineers should be thinking about scope, experimentation, and the word “no.”
The discipline of no was always a response to a resource constraint. Time, attention, and capacity were finite; code was expensive to produce and maintain; therefore, the rational strategy was to minimise the surface area. Everything you said no to protected the team’s ability to sustain the things they had already said yes to.
When the cost of production drops substantially, the calculus changes. Not for everything. The maintenance cost is unchanged, and that is the part that accumulates. But the experimentation phase is different. The phase before you have decided what to commit to. The phase where you are still validating whether the approach is the right one.
The teams that are working this out successfully are not saying yes to more scope. They are saying yes to more attempts. They are running experiments they would previously have ruled out as too expensive relative to the information gained. They are exploring solution spaces more thoroughly before converging. They are failing faster, earlier, and more cheaply, which is what “fail fast” was always supposed to mean, before it became a slogan untethered from the economic conditions that make it viable.
The important discipline here, and this cannot be overstated, is maintaining a clear distinction between the exploration phase and the commitment phase. The reduced cost of writing code makes it tempting to blur that boundary: to let the prototype become the production implementation, to carry the experimental code through into the system without the review and hardening it requires.
This is exactly the dynamic that produces the worst outcomes from AI-assisted development. I’ve written about this as vibe coding elsewhere: a legitimate prototyping technique that becomes dangerous when it quietly becomes a development methodology. The cheapness of the attempt does not change the requirements for what ships.
What changes is that you can afford to throw away more attempts before you find the one worth keeping. That is a genuine shift. Used well, it means better solutions, explored more thoroughly, selected with more evidence. Used carelessly, it means faster accumulation of debt that was never meant to survive.
The Bottleneck Shifts to Judgment
If code becomes easier to generate, the scarce resource in the system shifts. It was always possible to produce more code than a team could sustainably maintain; the discipline of “no” was the mechanism for preventing that. What changes now is that the bottleneck is less often the production of the code and more often the evaluation of it.
- Someone has to read what was generated and determine whether it is correct, appropriately structured, handles the edge cases, and is free of security risks the generator didn’t recognise.
- Someone has to decide which of the three experimental approaches is worth carrying forward.
- Someone has to catch the prototype that has quietly become load-bearing before it was reviewed.
These are all judgment calls. They require exactly the kind of accumulated understanding of systems, failure modes, and the specific context in which the code will live that cannot be generated. It can only be developed through time and exposure.
There is a risk buried in this argument worth naming directly. In his recent piece, “Agentic Coding is a Trap”, Lars Faye observes that agentic coding creates a self-defeating cycle: supervising AI-generated code effectively requires the coding skills that heavy AI use tends to erode. Anthropic has acknowledged this tension in its own documentation: using AI well requires supervision, and supervision requires the very proficiency that over-reliance on AI may atrophy. If engineers stop engaging deeply with code, they gradually lose the ability to evaluate it. The permission to try is only valuable if you retain the judgment to decide what’s worth keeping. That judgment is not a fixed asset; it has to be actively maintained. Which means the answer is not to avoid AI assistance, but to stay close enough to the code that you never lose your ability to read it critically.
The implication for how we value engineering expertise is significant, and it runs against the current market trend. At the moment when AI has made code generation easier, some organisations are interpreting this as evidence that the people who do the evaluation and direction are less necessary. The logic inverts: if code is cheaper to produce, surely you need fewer people producing it.
What gets missed in this reasoning is that the bottleneck has moved. You need fewer person-hours generating code in the way that typing a letter requires fewer hours when you have a word processor. The activity that still requires human judgment, deciding what to write and whether what was produced is any good, remains exactly as demanding as it was. It has simply become a larger proportion of the total work.
This is consistent with something I’ve written about in other contexts: AI amplifies what’s already present. Capable engineering teams, well-organised and with experienced judgment distributed across their membership, find that the changed cost equation accelerates them. Poorly-structured organisations find that it accelerates the accumulation of problems that were already there. The variable is the quality of judgment available to evaluate and direct the tools, which is not the variable the hiring market is currently pricing correctly.
Local Models and the Democratisation of Experimentation
There is a dimension to this shift that hasn’t yet been widely discussed, and it changes the economics in a second significant way.
For the past few years, AI-assisted development has largely meant API tokens. Every completion, every generation, every query against a coding assistant runs against a cloud service and costs something. For individual practitioners and small teams, this creates a different kind of constraint: not the time cost of writing code, but the financial cost of generating it. Experimentation becomes expensive in a different register.
This is changing, and faster than most people outside the practitioner community have noticed.
Modern Apple Silicon, specifically the M3 and M4 series chips with their unified memory architecture, can run capable coding models locally with reasonable performance. A machine with 32GB of unified memory can run models in the 14–34 billion parameter range comfortably. Ollama has made the deployment of these models on a developer’s own machine close to trivial: a single command installs the tool, another pulls the model, and you have a local inference server running within minutes.
The models available for local use are not the frontier models. Compared to the current generation of hosted models, they lag on reasoning complexity, context handling, and performance on novel or ambiguous problems. But for a substantial class of routine development tasks, including generating boilerplate, writing tests for known patterns, refactoring within a file, exploring a new API surface, and producing documentation, models like Qwen2.5-Coder 14B, Codestral, and the smaller distillations of DeepSeek-R1 are genuinely capable. Good enough to be useful. Good enough to change what’s worth attempting.
The pattern that is emerging in teams that have started working this way is a tier model:
- Local inference for the high-frequency, lower-complexity tasks: the code you generate dozens of times a day, the completions you want instantly, the refactors that don’t require deep reasoning.
- Cloud inference for the tasks that benefit from frontier-model capability: the architectural questions, the multi-file refactors where deep context matters, the genuinely novel problems where reasoning quality is the difference between a useful answer and a plausible-sounding wrong one.
This has several implications worth examining.
For small teams and solo practitioners, it removes the per-token cost anxiety from routine experimentation. You can run a spike without watching the API bill. You can try multiple approaches without the cost of each attempt accumulating in ways that make you think twice before generating. The economics of exploration described earlier, the ability to say yes to more attempts, become accessible without the financial overhead.
For organisations with strong data privacy requirements, local models provide a path to AI-assisted development that doesn’t require code leaving the machine. This has been a genuine blocker for teams in regulated industries or working with sensitive intellectual property. It doesn’t resolve all such concerns entirely, since the most capable assistance still lives in the cloud, but the barrier to routine AI tooling drops significantly.
The quality gap is real and should not be minimised. Knowing which tier of tooling is appropriate for which task is itself a judgment call that requires experience. The engineer who defaults all their AI queries to the local model will occasionally get confidently wrong answers on tasks that needed frontier reasoning. The engineer who defaults everything to the cloud API will spend money they don’t need to on tasks a local model handles perfectly well. The skill, like most of the skills that matter more in an AI-assisted world, is evaluative.
What This Means for Teams
The implications for how engineering teams operate are worth thinking through specifically, because this is where the changes become actionable.
The economics of exploration. If spikes are cheaper and cross-stack experiments are more accessible, teams can afford to run more of them before committing to architectural decisions. This is worth building into how you plan and estimate. Time-boxing exploration phases that would previously have felt extravagant now makes sense. The information gained from a three-hour spike that rules out an approach is often more valuable than the confidence that would have come from committing to that approach without testing it.
The gate between experiment and production. The flip side of cheaper exploration is the risk that experimental code crosses into production without the review it requires. This is not a hypothetical; it is the pattern that produces most of the technical debt I observe in teams that have adopted AI tooling without adjusting their process. The discipline required is explicit: what is the mechanism by which code that was generated experimentally gets reviewed before it carries production responsibilities? Teams that answer this question clearly tend to extract the value. Teams that don’t answer it tend to accumulate the problems.
Onboarding. One of the less-discussed applications of AI in a team context is the bridge it provides for new joiners, and this is more significant than it might initially appear. The knowledge gap a new engineer faces when joining an established team is not primarily a gap in technical skill. It’s a gap in context: why was this decision made, what does this component do, what are the conventions this team has adopted, what does the business actually need from this service?
That context has traditionally lived in people’s heads, transmitted through pairing, through pull request feedback, through the slow accumulation of conversations. A new engineer with good AI tooling can query the codebase, ask why decisions were made, understand what the system is trying to do, and navigate the architecture without needing to pull a senior engineer away from their work to give the same orientation they’ve given three times before. The ramp from “new joiner” to “meaningful contributor” compresses.
The condition, and this matters, is that the knowledge has to have been captured somewhere. AI cannot surface institutional knowledge that lives only in memory. If the context exists exclusively in the heads of the people who built the system, a new engineer with AI tooling is no better off than one without it.
But teams that have invested in capturing context (architecture decision records, structured documentation, detailed CLAUDE.md files, explanatory notes on non-obvious decisions) find that the work they did to make AI tools useful is the same work that makes their codebase legible to new joiners. The two benefits are the same investment. That’s a compounding property worth understanding.
It also shifts what senior engineers spend their time on. Less time answering the questions that a well-documented codebase and capable AI tooling can answer. More time on the decisions that require genuine experience: evaluating which generated approach is worth keeping, catching the architectural drift that happens when multiple developers are generating code without a coordinating vision, mentoring in the direction of judgment rather than syntax.
What This Means for You
For individual engineers, the shift is worth sitting with, because it runs against instincts the profession has spent decades cultivating.
Your range has expanded. If you have deep conceptual understanding of the systems you work with, if you know how distributed systems fail, how databases execute queries, how memory allocation works, how event-driven architectures handle backpressure, you can now apply that understanding in technology contexts you haven’t previously worked in commercially. This is not a licence to claim expertise you don’t have. But it is a genuine shift in what’s accessible to an experienced engineer who is willing to work at the interface between their conceptual knowledge and AI-assisted implementation.
Evaluation matters more than production. Reading generated code and determining whether it is correct, idiomatic, appropriately structured, and safe is a different activity from writing that code from scratch, though it draws on the same underlying knowledge. The engineer who can generate quickly but can’t evaluate reliably will produce code that looks right but isn’t. The engineer who evaluates carefully and directs well will extract good outcomes from the tools regardless of their raw generation speed.
Ask “what should we try?” before “what should we build?”. Not as a replacement for rigour, but as the appropriate application of cheaper experimentation to the problem of making better decisions earlier in the process.
“Should we keep this?” is the new “should we write this?”. The discipline of that question remains as important as it ever was. The word “no” hasn’t retired. It’s just moved to a different point in the conversation.
The Point of the Permission
The conversation I described at the start of this piece didn’t end with a clear set of conclusions. It ended, as the best of these conversations do, with a clearer articulation of the question.
The question is not whether AI will write all our code, or whether engineers will become obsolete, or whether the tools are good enough yet. Those questions are either already answered or they are distractions from the more immediate one.
The question is what the changed cost of writing code means for how experienced engineers make decisions about what to attempt. And the answer is that the permission to try has expanded significantly: the permission to explore, to spike, to fail cheaply and early, to cross into unfamiliar stacks with your conceptual knowledge as the currency rather than your syntax fluency. To start more things, knowing that starting has become cheaper, and that the discipline of deciding what to finish can remain as rigorous as it ever was.
That permission doesn’t extend to shipping carelessly. The maintenance cost hasn’t fallen. The judgment required to decide what’s worth keeping hasn’t become less demanding. If anything, it’s become more central.
But the discipline of “no”, the reflex that protected teams from complexity they couldn’t sustain, was always a response to a cost constraint. That constraint has changed shape. The teams and individuals who recognise it clearly, and adjust accordingly, will find that the changed equation works significantly in their favour.
The ones who don’t adjust will keep saying no to the same things they always said no to, unaware that the economics that made those constraints rational have quietly shifted beneath them.
About The Author
Tim Huegdon is the founder of Wyrd Technology, a consultancy focused on helping engineering teams achieve operational excellence through strategic AI adoption. With over 25 years of experience in software engineering and technical leadership, Tim specialises in the strategic shifts that AI introduces to engineering practice: not just what the tools can do, but how they change the decisions teams make about what to attempt, what to build, and what to ship. His work focuses on helping engineering leaders and individual practitioners understand where the real bottlenecks lie in an AI-assisted world, and how to cultivate the judgment that makes experimentation valuable rather than just fast.