Adapting to AI: What Is Software Engineering?

Adapting to AI: What Is Software Engineering?

Dave Farley’s book Modern Software Engineering explores the question: Is software engineering real engineering? Farley’s conclusion is yes, when the work is performed by applying a set of broadly accepted and repeatable practices that scale and lead to quality outcomes. Unfortunately, a lot of software is developed, tested, and operated without following such disciplined practices. Farley categorizes this as craft software development rather than engineering. Craft is good at creating one-of-a-kind items with higher variance and low repeatability, while engineering practices are scalable and repeatable through controlling complexity and variance.[1]

I wish more people in the software industry, especially people managing software teams, would read this book, and carefully consider, from first principles, if they are practicing software engineering.[2] This book was written before the rapid changes from the wide-scale adoption of AI in the software industry, which I started writing about in my essays Adapting to AI: Reflections on Productivity and Adapting to AI: Code Review. In this essay, I will explore how AI is changing software engineering by examining software engineering practices through the framing in Farley’s book.

What Is Software Engineering?

Farley defines software engineering as the application of an empirical, scientific approach to finding efficient, economic solutions to practical problems in software. Software can be adapted over time, which makes software engineering a process of discovery and learning. When solutions become well established, it may be more efficient, reliable, and cost-effective to forgo the flexibility of software and implement them in hardware or firmware. Software engineering must be efficient and economic, so our ability to adapt and learn needs to be sustainable. Therefore, software engineering includes managing the complexity of software systems in ways that maintain their functionality, performance, security, and flexibility as they age, scale, and change over time. Software engineering is about the process—the approach to solving problems—not just the solutions themselves. Software engineering includes the tools, the culture, and the process as part of the whole.

How Will Software Engineering Change With AI?

Having defined software engineering, let’s explore how AI might change software engineering through the lens of the three remaining sections of Farley's book: how software engineering optimizes for learning, how software engineering manages complexity, and the tools that support software engineering.

Optimize For Learning

Farley believes software engineering optimizes for learning through iteration, incrementalism, experimentation, empiricism, and feedback. Because the cost of production is so cheap, when we can control the variables, software affords an opportunity to carry out experiments and provide feedback at scales unimaginable in other engineering disciplines. Working iteratively is a means of refining the entire system to be better over time. Working incrementally improves the system over time by continuing to add components or features to a system that is already providing value.

Automated acceptance tests, continuous integration, continuous delivery, systems design, modularity, and our ability to control, observe, and measure key variables are all important for experimentation, iteration, incrementalism, empiricism, and feedback. AI works extremely well if it has feedback, like a passing or failing test, to deterministically and autonomously evaluate its own work. This allows AI agents to experiment, working iteratively, incrementally, and empirically for hours at a time. I'm not sure AI really changes much other than drastically scaling this process. I expect AI to stratify results such that teams with excellent automated acceptance tests, fast, reliable, and repeatable deployment pipelines, superior visibility into production performance metrics, and so on, will perform far better than teams without. Organizations with many handoffs—for example, from product management, to software engineering, to integration engineering, to system test, to deployment, to operations—will perform far worse than organizations that have tight feedback loops and minimal handoffs that can be highly leveraged by AI.

My perspective on software engineering comes not just from building software systems that operate critical infrastructure, like manufacturing, power, and transportation, but from operating the software systems themselves. In production, these software systems become critical infrastructure in their own right—operational technologies (OT) rather than information technologies (IT). Distributed software systems are dynamical systems—messages have nominal arrival rates, diurnal patterns, or stochastic behaviors; messages are durably queued for at-least-once delivery so the dynamics can change drastically as services are redeployed or recover from a network partition and queued messages are rapidly consumed; contention for shared resources like CPU, network, memory, or disk also changes dynamically; and these software systems require fail-safes, redundancy, backups, and disaster recovery. At scale, there are dynamics that are hard to manage—like time-aligned behaviors from millions of clients you do not control that stress the limits of the system despite your best efforts to avoid them. Because dynamical systems are difficult to test and simulate at production scale, I appreciate Farley's emphasis that software engineering is an empirical engineering discipline, like chemical engineering, industrial engineering, and system engineering. Not only is it hard to reproduce the scale in simulation, it is nearly impossible to reproduce the diversity of a production environment.

A critical part of being an excellent distributed systems engineer is applying techniques to observe the state space, reduce complexity, avoid catastrophic failures, and safely run experiments in production environments using techniques for continuous improvement, as the Japanese made popular in lean manufacturing.[3] I am seeing AI improve operations by being able to quickly tease out conclusions from a huge amount of context, like analyzing log files across hundreds of servers to reconstruct a sequence of events that led to failure. AI is improving incident response by allowing people who may not be familiar with a particular system or behavior to quickly assemble more context, or quickly build tools that can be used for diagnosis or system recovery.[4] I'm also seeing AI being used to constantly monitor production systems for certain conditions. It is getting less attention, but the impact AI is having on operations is at least as significant as it is on programming. AI is not doing things humans could not also theoretically do, but it is drastically expanding what is possible because it has reduced the time and the cost.[5]

Optimize For Managing Complexity

Farley details how software engineering manages complexity using modularity, cohesion, separation of concerns, abstraction, and loose coupling. Modularity is the essence of software development. It is achieved through abstractions and loose coupling so that if one thing changes, everything else doesn't have to change. Cohesion keeps related things together, and separating concerns keeps unrelated things apart. Modularity isn't just about the code itself, but also how the code can be tested or deployed independently from other parts of the system. If we cannot control variables and clearly define system boundaries, the system becomes more risky and difficult to change over time. The reaction of many organizations is to attack this problem by testing everything together to be sure the system is safe for use. But this only makes the problem worse, decreasing modularity further and introducing more places for mistakes to hide.

We might view modularity differently with AI. With code now easier to produce, we might favor duplicating code to reduce coupling and minimize the scope of changes rather than using shared libraries. Rewriting code now takes less investment, so this trade-off has also changed. AI might also influence information hiding and abstraction since an AI can keep far more context than a human can. It is not that abstractions won't be important, but we might draw the boundaries differently. However, I don't expect AI to fundamentally change how we manage complexity. Modularity will remain as important as ever. As soon as there is coupling, there are constraints on how much work can be parallelized. The cost of integration is the killer and this applies equally to AI agents and humans.

Abstractions and modularity aren't just used to understand systems, or make systems deployable they also define what parts of the state space can be observed or controlled, the failure domains, security boundaries, isolation levels, scaling units—where systems can be scaled vertically or partitioned to scale horizontally—and much more. While AI will help engineers experiment with trade-offs faster and more comprehensively, the expertise of a practitioner will be more valuable than ever. More code equals more complexity, almost by definition. Teams that think in systems and are responsible for the full software lifecycle—including development, security, testing, validation, deployment, operations, incident response, capacity planning, costs—will understand if they are sufficiently managing complexity and choosing good abstractions far better than teams who only own a part of the lifecycle. Owning the full lifecycle makes AI a tool grounded in reality and focused on outcomes. These teams will also better leverage AI to build on top of existing abstractions, rather than always inventing new ones.[6]

I haven’t seen enough data on the influence programming languages have on the ability to control complexity with AI. I expect to see more research on this over the next year.[7] It is plausible that languages that have excellent run-time performance, compile-time memory safety, a good type system, avoid null pointer exceptions, and favor compile-time errors versus run-time errors will be better for managing complexity with AI. It is interesting to note that Boris Cherny, the creator of Claude Code, said the one technical book that had the biggest impact on him was “Functional Programming in Scala”.[8] He said types are more important than the code itself. We will see if AIs that leverage types—perhaps types closely related to business abstractions or operational boundaries—end up being more effective at managing complexity than ones that favor imperative solutions.

Tools to Support Engineering in Software

Farley believes the tools that support software engineering are testability, deployability, speed, controlling variables, and continuous delivery. Making code testable achieves all of the themes of the book: controlling variables, defining abstractions, providing feedback, supporting iteration, achieving appropriate coupling and modularity, etc. He believes testing must be automated—because people are too slow and too variable to get quality feedback—and incorporated into continuous integration and continuous delivery (CI/CD).

A great build system is a foundational practice for software engineering. The build doesn’t just include production binaries, but also the deployment artifacts, like immutable containers or virtual machines, static analysis, tests, CI/CD, and alternative builds, like binaries suitable for dynamic analysis. Software builds can take minutes or hours, depending on the programming language and the size of the project. A fast, secure, repeatable, and scalable build system that can be used by agents is the most important investment an organization can make to transform their capabilities with AI.

Improvements to build speed have always been appreciated by engineers because of the impact on productivity. With AI, build speed becomes an existential differentiator. The AIs of today work particularly well if they can work iteratively and empirically with a way to autonomously verify their work. An organization that can use AI to autonomously make changes, build, deploy, and test in a matter of seconds will far out-iterate one where this process takes minutes or hours. It won’t even be close.

Not only do build systems need to be fast, they also need to be much more scalable to accommodate many more agents than people. This includes scaling all processes that support the build, like evaluating your software supply chain to ensure the AI hasn’t included libraries that introduce new security vulnerabilities or undesirable licensing terms. As I wrote about in Adapting to AI: Code Review, AI affords a lot of opportunity to improve quality through deterministic simulation testing, formal verification, model checking, fuzzing, and more, and the build system will have to scale to accommodate this.

Conclusion

Farley defines engineering as the practical application of an empirical, scientific approach to finding efficient, economic solutions to practical problems. Software engineering is focused on optimizing for learning through iteration and experimentation, as well as managing complexity through abstractions and modularity. Abstractions aren’t just about programming a system they are also what defines our ability to test, deploy, operate, scale, compose, secure, and evolve systems in production. There is a lot more to software engineering than coding. Engineering practices are repeatable. Engineering practices scale.

Writing before the introduction of the current AI tools, Farley states:

If we were to succeed in defining an engineering discipline for software development, then it would be agnostic to technology.

AI may change the speed and cost at which we can iterate and experiment, and the breadth of tools we can invest in to control complexity, but not what defines good software engineering.


  1. Margaret Hamilton is credited with being the first person to use the term "software engineering": “I fought to bring the software legitimacy so that it—and those building it—would be given its due respect and thus I began to use the term 'software engineering' to distinguish it from hardware and other kinds of engineering, yet treat each type of engineering as part of the overall systems engineering process.” ↩︎

  2. I also wrote about Farley's book in my essay Is the Scientific Method Valuable in Engineering? and I provided additional resources for exploring the question: Is software engineering real engineering? ↩︎

  3. A Stanford study entitled Online Controlled Experiments at Large Scale found that only one-third of ideas tested improved the metric they were designed to improve. ↩︎

  4. A friend of mine recently used AI to reconstruct a packet capture from an IoT device. The packet capture was hundreds of megabytes in size and uploading it kept failing because the network connection was unstable—the unstable network was the reason for wanting the packet capture in the first place. My friend truncated the file so it was just the very beginning and the very end of the packet capture, a file small enough to upload. Tools for analyzing the packet capture couldn't make sense of the truncated file, of course, but AI was able to stitch it together almost instantly and identify that the server was closing the connection. This was an amazing use of AI to improve operations. ↩︎

  5. To build on the packet capture example in the previous footnote, an engineer could read the RFCs and write a tool to do the reconstruction, but most enterprises would not be willing to invest weeks of engineering time to do this. Instead, they are forced to accept the low quality of service for this customer or address it through a workaround. With AI making the development of such a tool essentially free, we can now more quickly and cost effectively go directly to the root issue, working from first principles. ↩︎

  6. Paul Stack provided suggestions for controlling complexity and improving repeatability with agents in the essay The Vibes Don't Scale. See also the paper SlopCodeBench: Benchmarking How Coding Agents Degrade Over Long-Horizon Iterative Tasks. ↩︎

  7. Some early research in Code for Machines, Not Just Humans: Quantifying AI-Friendliness with Code Health Metrics suggested human-friendly code is also more compatible with AI tooling. ↩︎

  8. See Boris Cherny on Ryan Peterman's podcast. ↩︎