August 11, 2026
While Go is known for its simplicity, speed, and scale, you’ve stated that the primary goal in creating the language was figuring out how to effectively program multicore networked systems. Why was this an important challenge at the time? How has the development of cloud computing coincided with Go’s growth?
The initial goal for Go was to provide a better programming environment for the kinds of software engineering being done at Google circa 2007. That software involved a large amount of code, a large number of engineers working together, and large numbers of networked machines, each with large numbers of cores. All of those kinds of scale were poorly served by existing environments, and we wanted to build something better to support our own work. Starting from scratch, let us rethink other fundamentals too, like dependency management, and revisit core implementation details, like low-latency garbage collection.
It’s no coincidence that Go is good for cloud computing, because that’s what it was designed for. William Gibson famously said, “The future is here. It’s just not very evenly distributed.” In 2007, the cloud computing future already existed at Google, and we designed Go to be part of that. As the cloud future became more widely distributed, Go did too.
Will you share some unexpected insights from your decade of leading Go?
In software engineering, the most fundamental truths don’t seem to change. The context around them does, but most of all it seems like we spend a lot of time remembering or rediscovering things we’d forgotten. Here are three:
Simplicity is the best strategy for scaling. When engineers think about how to solve a complex problem, the obvious answer is to design a complex solution, one with good asymptotics but high fixed overheads. If instead you look for ways to simplify the problem, that often leads to simple solutions with low overheads that work just as well. Simpler solutions scale better and are easier to maintain. For example, C++ and Java build systems have many problems caused by cyclic dependencies between libraries. Cycles create large units that have to be processed all together, and lots of work has gone into complex solutions for scaling that. For Go, we disallowed cycles. If A imports B, then B cannot import A, not even indirectly. Forcing the package graph to be acyclic makes builds faster, makes analysis tools faster, and is easier for people to understand. The restriction is occasionally annoying, but the benefits far outweigh any inconvenience.
Software engineering is what happens to programming when you add time and other people. Programming is when you have a problem to solve, you write some code, you run it, you fix the bugs you find, and you’re done. Programming is already hard. But what if that code has to keep working, day after day, for years, even as you update it? What if many other programmers need to work on the code too? Then you need version control systems, bug trackers, tests, module boundaries, design patterns, and more. All of that comes in because your programming has turned into software engineering. Schools can teach programming, but they don’t have the time horizon to teach real software engineering. It’s something you only learn by experience.
Tests are more for tomorrow than today. Tests that are checked in alongside our code are not for proving the code correct today. They are for being able to check that it’s still working tomorrow, and the next day, and the next year, even as the code evolves. If we only cared about the code working today, we could test it by hand until we were satisfied. And then each time we revisited that code we could think very carefully about the changes we were making and manually test what might be affected. This strategy does work, but it doesn’t scale. If you have automated tests that you can re-run at any moment, and if those tests check all the important behaviors of your code, then you can guarantee that those behaviors never break, simply by running the tests! You can evaluate a new compiler or a new version of a dependency library. You can refactor or rewrite complicated code. You can more easily accept changes from engineers (or coding agents) unfamiliar with the system. Almost everything about software engineering becomes easier in proportion to how good your tests are.
Although some have predicted that AI will usher a fundamentally new paradigm in software development, you’ve remarked that “continuum” would be a better word for describing AI’s impact. How do you foresee the coming transformation?
I said on an ACM ByteCast episode that if you compare today’s computers with the ones I had as a kid in the 1980s, they are qualitatively different in almost every way, but that change happened one small step at a time. I expect the impact of AI to be like the impact of all the other changes in computing: many small steps that add up over time to something qualitatively different, not the single incomprehensible leap that some people are hyping. That’s an important distinction, because it means that as we take those steps, we still have time to decide what AI is good and not good for, and how best to use it, both as software engineers and as a society.
For software development, I don’t believe AI invalidates the fundamentals of software engineering. On the contrary, I think software engineering fundamentals can help us better understand what AI will be good and not good for.
One example is Peter Naur’s 1985 paper “Programming as Theory Building.” Naur talks about the importance of having a coherent vision or theory of how real world demands map into the program’s design and structure, He makes the case that use of a consistent theory over time is what keeps programs simple. He also argues that this kind of theory can never be completely written down or communicated, because the most important aspect is how it deals with unanticipated new demands. Every engineer has a story about designing major changes throughout a program to handle a new requirement, only to have the original author point out a trivial alternative they’d never have considered. Peter Naur argues that the underlying theory is fundamental to maintaining the program:
“The death of a program happens when the programmer team possessing its theory is dissolved. A dead program may continue to be used for execution in a computer and to produce useful results. The actual state of death becomes visible when demands for modifications of the program cannot be intelligently answered.”
An implication is that the program is not going to work very well to let AI agents with limited memory and no actual persistent theory make changes over and over in a code base. The AI agents can produce working code, just as energetic fresh engineers can, but far too often the result is more complex than it needs to be. It lacks coherence and simplicity because the theory is missing.
As another example, John Ousterhout’s 2018 book, A Philosophy of Software Design, outlines the difference between tactical programming, which means focusing entirely on getting something working today, and strategic programming, which means investing time to improve the design of the system to handle the new requirement cleanly. There are appropriate times for both, but the usual failure mode results from too much tactical work and not enough strategic work. Ousterhout introduces the term “tactical tornado” for a programmer who churns out tons of working but overly complex code that doesn’t fit well into the existing system. Worse, a bad manager often sees a tactical tornado as the most productive programmer on a team, failing to recognize the complexity and code debt left behind for the rest of the team to clean up. If we aren’t careful managers, AI agents can easily become the ultimate tactical tornadoes.
As we explore the use of AI coding agents over the next few years, I expect we will need to rediscover other fundamental truths of software engineering: things we already know but are currently trying to hope, wish, and dream away.
Will the framework universities use to teach software engineering need to change? If so, how?
Absolutely. Schools and universities use papers, coding assignments, and tests as indirect indicators for the goals that really matter, namely mastery of skills, knowledge, and new ways of thinking. It used to be that if you could get a basic operating system coded and running, that was an indirect proof that you understood the basics of how operating systems worked. The proof was never perfect, of course: maybe the assignment was overly scaffolded, or maybe a lab partner did most of the work, or maybe you cheated by copying last year’s solutions. What those failure modes all have in common is eliding the friction, discomfort, and struggle where the actual learning happens. Generative AI adds a trivial new way to avoid learning, and it doesn’t even have to be the blatant “write my code for me.” If you write your own code but then always ask the AI to find and fix your bugs, you’re still bypassing the struggle and the learning.
I don’t think anyone knows exactly how teaching will need to change. It’s tempting to say that we should embrace AI coding agents and use them to expand what’s possible. For example, instead of building one operating system in a class, maybe you could prompt the AI to build a few very different ones and focus on evaluating and comparing them. That seems like an attractive idea, but I’m not convinced there’s enough effort directed at real learning about the operating system topics, as opposed to effort directed at how to effectively prompt the AI agents. Also, you can do that comparison without AI. The MIT operating systems class I helped create studies the source code for a traditional monolithic kernel while students build their own microkernel in the labs. On the other hand, grading can’t be based on working code anymore.
Ultimately, we have to identify what we actually want to teach, and perhaps AI really will help us skip over unimportant details that we couldn’t before. But for the important details, I think we fundamentally have to insist on the discipline to struggle with that material directly, or else the learning doesn’t happen. It doesn’t accomplish anything to bring a forklift to the gym.
As part of your volunteer service, you are a member of the ACM Queue Editorial Board. Why is the magazine an important resource for the software engineering community?
I think it goes back to the William Gibson quote cited above. The near future of software engineering and software systems is here already, in both recent research and recent practical advances. Queue’s goal is to make those parts of the future more widely known. We identify important topics and then publish in-depth treatments by experts. As time goes on, that approach seems to become less and less common, but that only makes it more and more valuable.