The promised productivity improvements of AI are hard to ignore. The industry is awash with claims that those who fail to embrace it are doomed to fade into irrelevance.

Company execs are rushing to arm their developers with the latest and greatest in AI tooling, often at significant cost, in the hope that they can realise these gains and produce more, faster.

But is this splurge on AI tooling actually making a difference? Are engineers getting more done, or is it just a marketing exercise by frontier labs looking to sell more tokens and support their lofty valuations?

The room seems divided.

On one side, AI makes some feel like mythical 10x engineers, able to compress into days what would have taken weeks. Others are sceptical, arguing that steering the AI can take as long as doing the work yourself.

How can these two views be reconciled?

This debate doesn’t surprise me. Over the last few years, I’ve worked on teams at both ends of the AI optimism spectrum.

At one end are teams trying to shoehorn AI into their existing, human-first workflows. They may be able to generate code faster, but they remain constrained by the systems and processes built around human developers.

At the other end are teams that have fully realised the potential of AI and have become its greatest proponents. They don’t see AI simply as another tool, but as a fundamentally different way of producing, deploying, and operating software. In response, they have rebuilt their workflows and organisations around the new reality: software will increasingly be authored and maintained by machines, not humans.

This divide between those who incrementally adopt a new technology and those who fundamentally reorganise around it is not unique to AI. It’s something which has been observed repeatedly in past technological revolutions.

Paul David, who studied the electrical revolution, described this as the Productivity Paradox, where despite massive initial investments in a new technology, productivity gains were slow to materialise. Early adopters simply “lifted and shifted” electric motors into factories, replacing steam engines while preserving the complexity built around them, from central drive shafts to elaborate pulley systems. It was only when factory owners recognised that electric motors weren’t simply better steam engines, but a fundamentally different way to power machines, that they began redesigning their factories around them. By giving each machine its own motor, they could abandon centralised drive shafts and reorganise production around more efficient linear assembly lines, finally realising the electric motor’s promised potential.

The advent of AI sees a similar story playing out. Many are simply lifting and shifting AI to their existing workflows, while few are rethinking what it means to be AI-native.

As machines increasingly author and operate software, we must design systems and workflows that they can autonomously understand, learn from, and act within. Three properties are central to achieving this: context, feedback, and agency.

Context

A key factor in AI’s effectiveness is how easily it can acquire the context needed to perform a task. Our systems should therefore be optimised to make that context as accessible as possible.

One implication of this is the advantage of a monorepo over a polyrepo. Polyrepos have traditionally offered some advantages like providing better separation between teams in large organisations, and allowing easier scaling of repo operations such as CI/CD etc.

Yet in the AI era, context is really (nearly) all you need: in large teams, there can be hundreds of repos in a mid to large org. Determining which repositories need changing to implement a feature requires a deep understanding of the wider system, and agents can easily miss dependencies or overlook repositories that need updating. The result is a higher risk of incomplete changes, more failures and greater need for human supervision.

In contrast, agents operating in a monorepo benefit enormously from being able to grep across the entire codebase, understand dependencies between services easily, and make changes across services in a single workspace. They can more easily run tests that verify their work end-to-end and generate code in keeping with global style conventions.

At $WORK, the benefits of having a monorepo have been abundant. Agents can autonomously create large changes in long-running sessions ranging all the way from the UI to the database layer, and even down to the infrastructure layer (via Terraform). These changes can be verified together and then split up into smaller PRs for easier review.

We’ve even recently decided to bring our documentation, which previously lived in a separate repository, into the main codebase. While I feel docs should remain hand-written (good docs require thought, clarity, and understanding: there’s nothing worse than reading lengthy docs written in excruciating Claudish that don’t really say anything of substance), they provide agents with a richer source of context, allowing them to reason not just about how the system is implemented but also how it’s intended to work. It also means documentation can benefit from the same tooling and automation as the code itself. For example, we can run LLM-driven checks that compare our docs against the underlying implementation, identifying outdated or inaccurate sections as the code evolves. Keeping code and documentation side by side makes it much easier to ensure that both remain in sync.

Thus, in the AI-era, a monorepo becomes a significant advantage.

The same principle of making context easily accessible extends to operations and observability. Pre-AI, having logs, metrics, and traces spread across disparate systems presented a mild inconvenience for engineers. It made correlating events harder but was tolerated when cost savings justified it.

With AI, this fundamentally limits the agent’s effectiveness when conducting autonomous investigations. Instead of querying a single backend, the agent has to call multiple tools and manually stitch together streams from multiple systems. This results in context bloat and more noise, leading to more hallucinations.

At $WORK, we’ve invested in making Datadog our single pane of glass. It exposes a CLI and MCP server where agents can query logs, metrics, traces, infrastructure events and even profiling data. Having a single backend to query means there’s far less context to stitch together, resulting in cleaner investigations and more reliable results. As the models have improved, an increasing number of incidents such as deployment issues, on-call investigations, and performance regressions can be handled completely autonomously. When a monitor fires or a deployment fails, the bot can query Datadog, inspect the monorepo, correlate signals across logs, metrics, and code changes, and identify the root cause.

For example, in a recent case, a customer was using a particularly old Kafka client library. It had a subtle difference in behaviour that, given how our fetch logic worked, could cause it to stall. Because we had client library versions and detailed fetch statistics available in Datadog, an agent was able to correlate the two and independently identify the particular client version responsible. Within an hour, we had communicated the root cause to the customer and were able to help them resolve it. Without access to this level of context, the agent could easily have got lost in the noise, leaving an engineer to manually root cause. That investigation could otherwise have taken many hours.

Feedback

Context gives the agent the information it needs to act, but an agent also needs to know what it has done is correct. A large part of why agents have been so successful with software is that the domain contains many tasks that are verifiable. This verification acts as a form of feedback that allows the agent to self-correct and converge to the desired behaviour. We should therefore strive to shorten the agentic feedback loop, providing as much feedback to the agent as quickly as possible.

This has implications in programming language choice. Traditionally, languages have been picked primarily with developers in mind. Aspects such as human readability, happiness, expressiveness, and developer skillset were prized. As models improve, machines are now becoming the primary authors and audience of code, with humans increasingly concerned with controlling the ideas, not the code.

It’s time to rewrite the old SICP adage: Programs must be written for machines to understand and only incidentally for humans to read.

In my experience, a static type system, which gives an agent feedback at compile time, is a huge advantage, even if it comes at the expense of increased verbosity. Languages like Go, which ship with robust tooling such as a built-in testing and benchmarking framework, make it much easier for an agent to verify its changes. Standardised linting and formatting tools also help constrain agents to codebase conventions.

I’ve seen the value of this feedback loop in practice. Recently, while I was knee-deep in a project, profiling revealed a regression in memory usage in our produce path. An agent was able to use Go’s benchmarking tools to reproduce the regression independently, investigate the change and trace it back to an underlying regression in a recently upgraded AWS SDK, that caused it to allocate an unnecessary buffer. Having reliable benchmarks also meant we could verify that the subsequent fix actually solved the problem.

More generally, I’ve found that languages that favour explicitness over implicitness, configuration over convention, and have a rich standard library are much more amenable to an agent than those that favour implicitness, dynamic typing, and rely heavily on external dependencies.

Discussions about languages of the future increasingly prioritise what works for machines over how humans prefer to express themselves.

Agency

Having accessible context and fast feedback loops are of little use though if an agent does not have the agency to act on them. For agents to be truly useful, they need to be able to operate autonomously on an isolated copy of the system, without requiring human intervention.

Making this possible requires a few things.

Firstly, agents need a machine-friendly interface to the systems they access. This might mean building CLIs or MCP servers that expose existing capabilities in an agent-friendly way. In my team, for example, our deployment tooling, observability data, and even our internal staging environments are accessible through CLIs, giving agents the same capabilities that engineers have.

Agents also need somewhere to run. As models have become more capable, they’ve been able to maintain coherence and make meaningful progress over increasingly long sessions.

In practice, this changes how I think about delegating work to agents. Rather than asking an agent to make a change and then spending time steering it through the implementation, I can give it a goal and let it work independently in its own environment. It can make changes, deploy them to staging, run tests and inspect the results, iterating on its implementation without needing me to supervise every step. I can then come back to review the result.

I can therefore run multiple agent sessions concurrently, getting more work done in parallel. However, it makes little sense for these long-running and sometimes compute-intensive sessions to be tied to my laptop. Instead, I’ve found provisioning dedicated remote compute where agents can run in their own isolated environments, independently of my local machine, a lot more effective. Agents are no longer bottlenecked by my ability to repeatedly invoke and steer them, and instead become always-on autonomous workers that can be given a goal, access to the context and feedback they need, and a bounded environment in which to see it through.

Conclusion

Over the last few years, I’ve become increasingly convinced that benefiting from AI means much more than buying a Claude subscription. It means rethinking the assumptions behind our human-first processes, from repository structure and programming language choice to observability.

In my day-to-day work, I’ve seen how these decisions have allowed us to integrate AI much more deeply into how engineers work: from repo-wide LLM-driven code review and linting, to agents that autonomously investigate alerts during on-call, to systems that proactively detect anomalies in customer workloads. Together, these capabilities enable our team to work more efficiently, with humans and agents working in concert to build, operate, and improve our product.

The steam age of human-first software development is giving way to the electrical age of agent-driven software.