Software Engineering: Where It Went Wrong and What We Can and Should Do to Fix It
n5321 | 2026年1月28日 01:12
Every few months, it seems we have this argument in our field: Are we or are we not engineers? Is what we do engineering or isn't it? Sometimes the players, even on the anti-engineering side, are people who had really deep fundamental contributions in the early days of software engineering, like Tom DeMarco. My friend Dave Thomas, one of the co-authors of The Pragmatic Programmer, once wrote this: "Calling what we do engineering doesn't make us engineers; it makes us liars." Now, I don't think Dave would affirm this statement today. He wrote this on the Extreme Programming mailing list about 15 years ago, but it's one of two things he said around that time—about the time when he was involved in writing the Agile Manifesto—that really started the line of thinking in my mind that many years later led to this talk. So, I'm grateful to him for that.
These "software isn't engineering" arguments usually boil down to one of two things: either software development doesn't look like what I think engineering looks like, or software development feels more creative to me—it's more like an art or a craft or something else like that; it doesn't feel like engineering. Notice that those are really two ways of saying the same thing: "I have in my mind an idea of what engineering is like, and software engineering either feels different or looks different or the way people do it seems different, so I don't think it's the same thing." And yet, we keep calling ourselves engineers. We do it in job postings and job titles and org charts. Jeff—I don't know where he went, but Jeff's title is Senior Software Engineer or something like that, right? How did we get to this point? How did we get to this weird state where we can't even agree on what it is that we fundamentally do? There's this interesting cognitive dissonance in our field, and I'd like to try to get to the bottom of that.
The Origins: NATO Conferences (1968–1969)
Where did it start? Software development first began to be seriously talked about as engineering in 1968 in Garmisch, Germany, at a software engineering conference promoted and paid for by NATO, of all things. And then a year later, there was a follow-up conference in Rome. It was in Rome in 1969 that software engineering was really launched as an active academic pursuit.
The first conference, if you read the proceedings, was characterized by debate, by a certain kind of intellectual humility about how much they didn't yet know. The second conference, a year later, was characterized by a lot of certainty—and I would say, and some of the people involved in that conference would say, that it was premature certainty. People were pretending to know more than they really did.
Academic Software Engineering (which is what I'm going to call that kind of academic field that branched off from there; I'll sometimes refer to it as ASE for short) aimed at becoming like engineering. "We must be more like engineers." That meant requirements, specifications, analysis, design. Test was very formal, very analytical. Correctness came through eliminating errors early in the process before code was written, through a series of inspections and validations of those documents and specifications. It was striving to be like engineers. You know, think of the stereotype of what engineers do: building a bridge. They don't go out and start mortaring stones together; they sit down and draw plans and blueprints and validate those and iterate on this design, and they make a blueprint, and then they give it to laborers who do the construction. So, we should be like that.
The 50-Year Divide
So that's where it started, and I'm going to real quickly try to give you a big-picture view of the 50 years since then before we dive into the details. At that time in 1969, mainstream software development really didn't have any kind of unified view of itself. It didn't have a goal in mind for where it hoped to be. People were just writing software as best they could, often not very well. Projects had a really high failure rate in those days. There were a lot of people exploring better ways of doing individual little pieces of the puzzle: better algorithms, better programming languages, better machine architectures—all kinds of different things—but no real overarching view of what the field should look like.
Academic Software Engineering kind of set a target of this idealized view of engineering, and they set off in that direction. They had some success with this. They defined some "rationale"—and that's a technical term we'll come back to later—they defined some rational processes for software development. Waterfall is one example, but not by any means the only one, although they all had that same kind of spirit of phases. But Academic Software Engineering fell short of its goal. Those processes were very costly and time-consuming and often not really any more reliable than the old way.
Mainstream software engineering, on the other hand, in the meantime continued more or less as it was and didn't pay much attention to the Academic Software Engineering results. Some large companies tried to follow these processes. I call it Academic Software Engineering because it mostly didn't escape from academia and ever get adopted by industry, but some large companies who could afford to gave it a really serious try, and despite the high cost, they still had a very high failure rate.
Lessons from the Trenches
Alistair Cockburn in the mid-90s spent a lot of time interviewing participants on projects that had tried these things and other processes. He had a really interesting set of three questions that he would ask these project participants.
The first one was: "Was your project a success?" Most of the time, but not all the time, he got fairly good clear yes or no answers for that, but sometimes it was a little more vague.
The second question was: "How strictly did you adhere to the process that you chose for this project?" Interestingly, he got very different answers from the project managers and leaders of the project than he did from the programmers in the trenches, as it were. Often the managers thought they were originally adhering to the process, but in fact, they weren't.
And the third question, which is the genius one, is: "Would you willingly choose this process again for a new project?"
Very often the answer was no. From the answers to these questions, Alistair built a very compelling case that in those days, even the projects that succeeded using the ASE processes succeeded more in spite of the processes than because of them. They succeeded because sharp people who cared were willing to circumvent the process and short-circuit it in some way in order to get things done.
The Emergence of Agile
So now, meanwhile, mainstream development carried on not paying much attention to this, and this led to a strange situation where we had a field where the term "engineering" was reserved for a set of practices that everybody knew didn't work. That's weird, because in every other field, that term is reserved for practices that people have demonstrated work pretty well. This is partly what led to the divide we see today where a lot of people are just like, "Software development is incompatible with engineering; don't even try."
Well, in the mid-90s, as a reaction to this weird situation, a number of people started trying to find other more appropriate disciplined ways of building software—ways to do a better job, have a higher success rate, meet customers' needs more reliably, but in a way that didn't cost so much. It was more appropriate for our field. That was the Agile movement, and it really solidified in 2001 with the publication of the Agile Manifesto. Where Academic Software Engineering was very analytical, Agile, on the other hand, was iterative and experimental. All of the phases or activities were performed iteratively and in concert with each other. Correctness was achieved by feedback and testing over time. The mainstream has bent towards the Agile methods with increasing success, and the Academic Software Engineering has continued, but as its influence has waned.
The Rational Model Misunderstanding
That has led us to this situation where we have two views: one is "Software development should be more like engineering," and the other is "Software development simply isn't like engineering, so we should stop trying." My view is that both of these views are mistaken because they are both based on the same core misunderstanding. That misunderstanding was that idealized view of engineering that Academic Software Engineering started out in pursuit of.
In the same year that the Rome conference happened, in 1969, Herbert Simon published a book called The Sciences of the Artificial, and in it, he defined a "rational model of decision-making." In 1986, in a paper he wrote called "A Rational Design Process: How and Why to Fake It," David Parnas—an important software engineering researcher—described how that model maps onto software development. This is a little tongue-in-cheek, but only a little. Parnas wrote that if you were pursuing the rational model, you would:
First, establish and document requirements.
Design and document the module structure.
Design and document the module interfaces.
Design and document the uses hierarchy.
Design and document the module internal structures.
Write programs and maintain.
Again, a little tongue-in-cheek, but it's not far off from the idealized view of engineering that Academic Software Engineering started with. Both sides of the arguments we're having today generally accept that the rational model accurately describes the engineering ideal; they differ in terms of whether they think we should or even can try to emulate that in the software world.
My view is different. I claim that software development already is an engineering field. It always has been. I mean, think about it: we are seeking and developing disciplined, reliable, cost-effective ways of building useful things for people at large scale. What else could it possibly be? Interestingly, other engineering disciplines really haven't had this kind of existential crisis about whether they're engineers or not; they just get down to engineering, sometimes badly, and have to learn hard lessons, but they don't worry about whether they're engineering or not.
The rational model, as it turns out, is a caricature of engineering. No branch of engineering—even the most established and analytical and formal of them—really works that way. And some established branches of engineering don't look like that at all. We don't need to "become" engineers. What we need to do is to continue to refine our own engineering practices and discipline, as all engineers are always doing.
Critiquing Academic Software Engineering
But accepting that is a lot to ask of you. I'm basically saying that an entire large, heavily funded academic field went down the wrong path from day one and continued down that path for 30 years at least. The least I can do is to try to back that claim up. And so the rest of the talk, we'll dive into the mistakes that analytical or academic software engineering made, we'll talk about what engineering really is like in the other disciplines, finally look at what software engineering is in its best form today, and then look a little bit at what next.
Where did Academic Software Engineering go wrong? First of all, there were objections there from the beginning that were ignored. This is Brian Randell. Randell is a noted professor of computer science, now professor emeritus at Newcastle University in the UK. He was the assistant editor of the conference proceedings at the first conference in Garmisch, Germany, and he was the lead editor of those proceedings in Rome. About 25 years later, in 1996, at the History of Software Engineering conference, he presented a memoir called "The 1968-69 NATO Software Engineering Reports." I'm going to read for you a lengthy quote from that memoir.
He wrote: "Unlike the first conference at which it was fully accepted that the term software engineering expressed a need rather than a reality, in Rome there was already a slight tendency to talk as if the subject already existed. And it became clear during the conference that the organizers had a hidden agenda, namely that of persuading NATO to fund the setting up of an international software engineering institute. However, things did not go according to their plan. The discussion sessions, which were meant to provide evidence of strong and extensive support for this proposal, were instead marked by considerable skepticism... it was little surprise to any of the participants in the Rome conference that no attempt was made to continue the NATO conference series, but the software engineering bandwagon began to roll as many people started to use the term to describe their work, to my mind often with very little justification. I made a particular point for many years of refusing to use the term software engineering or to be associated with any event that used it."
Academic Software Engineering ignored important dissenting opinions from the early days. This is a quote from the Garmisch conference by Alan Perlis, a noted computer scientist from the early days: "A software system can best be designed if the testing is interlaced with the designing instead of being used after the design. A simulation which matches the requirements contains the control which organizes the design of the system. Through successive repetitions of this process of interlaced testing and design, the model ultimately becomes the software system itself." What is he describing here? Prototyping, test-driven development, iterative development, using mock services. This was 1968, and then in 1969, we went down a completely different path.
The Misplaced Science of Engineering
They began with the rational model and also a view of engineering as "Applied Science." Fred Brooks, the author of The Mythical Man-Month, also wrote another wonderful book called The Design of Design. In the preface, he talks about the rational model: "The rational model may seem naive, but it is a very natural model for people to conceive." Winston Royce made that same point in his paper that introduced the Waterfall method. He said, "This is very obvious, and this is how people are doing it today," and he then went on for the rest of the paper (which nobody ever remembers) saying why it was a disaster to build software that way and you should never do it.
Fred Brooks reminisced about studying engineering at Harvard in the 50s. At the time, the Dean of Engineering was John Van Vleck, a Nobel Prize-winning physicist, not an engineer by training. Brooks had this to say: "Van Vleck was very concerned that the practice of engineering be put on a firmer scientific basis. He led a vigorous shift of American engineering education away from design toward applied science. The pendulum swung too far, reaction set in, and the teaching of design has been contentious ever since."
Another mistake they made was to devalue the role of code in the software engineering process. At the Garmisch conference in '68, Friedrich Bauer said this: "What is needed is not classical mathematics but mathematics instead." In any case, systems should be built at levels and modules which form a mathematical constructor. Elaborating on this, Edsger Dijkstra said: "Our basic tools are mathematical in nature." This was in response to people talking about how engineers worked with equations and math and rules and they proved things that way. These two men were trying to get across the point that code and logic don't look like classical mathematics, but they are inherently mathematical, and there is a rigor to them that you can't escape. We don't necessarily need to go to classical mathematics to achieve the rigor that engineering system disciplines desire.
Another reflection of that: in 2007 at the OOPSLA conference in Montreal, I heard David Parnas give a talk reflecting on his career in software engineering. He said two things a short distance apart in the talk that really struck me. First, he said, "In engineering, people design documentation," which is true. But a little later, he explicitly said that code can't fill that role for software, which is a serious error in my mind.
Finally, Academic Software Engineering disproportionately emphasized correctness over cost. Their primary concern was how to build correct, reliable, robust systems. Everybody who has any association with software engineering has seen the "cost of change" curve. This was based on research done by Barry Boehm and published in 1981. The idea is that the farther you go along in your project life cycle, the costlier it is to fix bugs. So the software engineering solution was: "Well, if it's cheaper to fix errors at the start of the project, let's do everything we can to find them as early as we can." But of course, doing this means extra care, extra inspections, extra reviews, and that has the secondary effect of increasing costs across the entire lifecycle. Organizations don't like that, so they pushed back, and this is like stepping on a toothpaste tube—it blows your schedule out. The cure was worse than the disease.
As an aside, all of the projects that Boehm studied for his original cost of change curve were waterfall projects. So decisions were made early, but they didn't actually integrate and test until very late. What he was actually measuring wasn't the cost of change during the project lifecycle, but the cost of long feedback loops. His results actually show that the longer you wait after you make a decision before you validate it and correct it, the more it will cost—which really should push us towards tight iterations and short feedback loops.
What Real Engineering Is Like
So those are the mistakes. What's real engineering like? Well, part of the problem is that engineers don't write about "engineering." A chemical engineer might write about chemical engineering, but there's been very little written about engineering per se as an umbrella. Billy Vaughn Koen, a mechanical engineering professor, said: "Unlike the extensive analysis of the scientific method, little significant research to date has sought the philosophical foundations of engineering... no equivalent body of research treats the engineering method." And that's mostly true. I've been looking for about 10 years, and so far I've found maybe eight books that even touch on what engineering is all about independent of a particular discipline.
Engineering is not like the caricature of the rational model. The clearest expression of the caricature I've ever seen came from a blog post by Bruce Eckel, where he wrote: "Programming is not some kind of engineering where all we have to do is put something in one end and turn the crank." Now, I don't want to be unfair to him; he's a smart guy, and I don't think he really thinks this is a description of engineering. It was a throwaway line on the way to a larger point he was making. But he said it, so I get to make fun of him for it. Let's be clear: there is no kind of engineering where all you have to do is put something in one end and turn the crank. Engineering is inherently a crude, creative, exploratory process marked by false starts and dead ends and failures and learning processes, no matter what discipline you see.
Billy Vaughn Koen describes it this way: "The engineering method is the use of heuristics to cause the best change in a poorly understood or uncertain situation within the available resources." That doesn't sound much like the caricature of engineering that we saw earlier, does it?
Another thing about engineering is that different engineering disciplines are very different. I'm going to use something called the "process control model," where you can describe processes as existing on a continuum from "defined" at one end to "empirical" at the other.
The defined process control model requires that every piece of work be completely understood. A defined process can be started and allowed to run until completion with the same results every time.
On the other hand, the empirical process control model provides and exercises control through frequent inspection and adaptation for processes that are imperfectly defined and generate unpredictable and unrepeatable outputs.
Interestingly, the chemical engineers were most amused when they learned that software engineering had chosen the "defined" end of the spectrum, because they didn't think it was appropriate at all. The various engineering disciplines fall all along this spectrum. Civil engineering and structural engineering are more toward the defined end—partly because they're older, but also because it costs a lot more to build a prototype. So it's really helpful to have formalisms to convince yourself of correctness before you spend billions. Mechanical and aerospace and chemical exist somewhere in the middle. Electrical and industrial engineering are much more empirical. And software engineering, I think, should be on the opposite end of the spectrum from civil and structural engineering.
Engineering as a Creative and Destructive Pursuit
Another fact about engineering is that it is a creative pursuit. Eugene Ferguson wrote: "The conversion of an idea to an artifact... will always be far closer to art than to science." Aerospace engineer J.D. North wrote in 1923: "Airplanes are not designed by science but by art, in spite of some pretense and humbug to the contrary."
In engineering, math and formal methods can be employed, but they're just tools to get the job done; they're not magic sauce. I think you probably all know Calvin and Hobbes. In my life as a dad, I've tried to model myself on Calvin's dad—inventing ludicrously wrong answers to amuse myself. One of my favorite cartoons is: "How do they know the load limit on bridges, Dad?" and Dad says, "Well, they drive bigger and bigger trucks over the bridge until it breaks. Then they weigh the last truck and rebuild the bridge. Simple." Why is this funny? Because it's ridiculous. It's also true.
How many of you have seen the video where Boeing engineers stress the wings of a Boeing 777 to the breaking point? They attach chains to the ends of the wings and start cranking them up until they break catastrophically. And then a very interesting thing happens: the engineers congratulate themselves. What are they happy about? They learned something. They learned that when it broke, it broke where they expected. Their math was right. They weren't really sure until they tried it.
Elon Musk, in a recent presentation about SpaceX, showed a tank they were designing. He said: "We successfully tested it up to its design pressure, and then went a little further, because we wanted to see where it would break." This is called destructive testing. This is really smart, and it's something that engineers do all the time. Remember that when people tell you that software isn't engineering because engineers have math to prove things so they know it'll work.
One last example: the Tacoma Narrows Bridge, which tore itself apart in a mild wind. It happened because "Deflection Theory"—the premier mathematical model at the time—was tragically incomplete. It accounted for downward stresses and sideways wind, but it didn't account for the fact that a thin deck can act as an airfoil. Vertical stresses started a vibration which settled in on the resonant frequency and eventually tore it apart. Remember this: engineers adopted formal methods to save money. The more accurate your model is, the less of a safety factor you have to build in. If building a prototype and testing it were instantaneous and free, they would do a lot less formal modeling.
The Practitioner-Driven Nature of Engineering
Unlike Academic Software Engineering, engineering disciplines typically are driven by practitioners. Innovation comes from the field and then is formalized and refined in academia.
Robert Maillart, a Swiss bridge engineer, got interested in reinforced concrete. He realized it had different properties than stone. He built bridges throughout Switzerland, but he did not have mathematics sophisticated enough to analyze these designs. He was soundly criticized by the establishment as a charlatan. What did he do? He prototyped. He built models at multiple scales, invited friends to jump up and down on them, rolled boulders over them, and extrapolated. Later, the math caught up.
The Catenary Arch: Robert Hooke demonstrated that the catenary shape was optimal for arches in 1671, but it wasn't until 20 years later that the equation was independently found. But it was known to be a useful form and was used by engineers for many years before that.
The Sagrada Família: Gaudí wanted it to feel like walking through a forest. He designed the arches based on hyperbolic geometry, but he didn't have math sophisticated enough to model them. How did he do it? He built elaborate upside-down hanging string models with weighted bags of sand to mimic loads. Later, the math caught up, and the current wave of engineers have validated all of this with computer models.
Redefining Software Engineering
This is the best definition of structural engineering I've been able to find: "Structural engineering is the science and art of designing and making, with economy and elegance, structures so that they can safely resist the forces to which they may be subjected." Notice the tensions there. There's always balance that needs to be resolved.
Based on that, I'm going to propose my definition of software engineering: The science and art of designing and making, with economy and elegance, systems so that they can readily adapt to the situations to which they may be subjected.
Early software engineering processes were designed according to a flawed analogy.
The Flawed Analogy: In structural engineering, you have an engineer who produces a design in document form, which is handed to laborers who build the structure. By analogy, we should have engineers produce a document handed to coders (laborers).
But this never worked. Coders had to finish the design, redesign, and fix stuff.
Let's rebuild this analogy in a way that works. What are our customers paying us for? They are paying for running systems on real machines. If we build this analogy properly:
The Source Code is the design document. It's the detailed design document.
The Computers (and compilers/language implementations) are the laborers. They take that design and turn it into something that solves the customer's problem.
Notice: the actual construction (compilation and deployment) is the cheapest part of our process. In Academic Software Engineering, everything was designed around avoiding the construction phase until you knew everything was right. But if that's the cheapest part of our structure, why don't we just do it all the time? There's a word for that: Continuous Integration. Every time you check in, the build kicks off, compiles the code, and runs the tests.
We have an upside-down engineering economics. The cheapest part of our process is building and testing a prototype. Compared to engineers who work with atoms, our prototyping is effectively instantaneous and free. So the right way to design a software engineering process is not to avoid this step, but to rely on it—to iterate, validating our designs with testing. This comes from a paper written in the early 90s by Jack Reeves called "What is Software Design?" His thesis was that the code is the design.
Where's the model in this? It's the code. Where's the document? It's the code. And our tools are mathematical in nature. Speaking of documents, David Parnas said code can't fill the role of documents. He developed "tabular mathematical expressions" for specifying logic. But we have documents that are like executable math—tools that allow you to write specifications that are self-validating and can test the system. We should embrace that.
Agile as Disciplined Engineering
One last thing: how does a software process feel disciplined and rigorous if it doesn't look like the rational model? If you look at the original practices of Extreme Programming, there is a hidden underlying structure. I built a diagram of these practices and realized they are a set of practices related to the scale of decisions they give you feedback about.
Pair Programming: Feedback on the level of statements and methods (seconds).
Unit Testing: Feedback on methods and classes (minutes).
Continuous Integration: Making sure it all hangs together (hours).
Short Releases: Letting the customer validate the direction (weeks/months).
Extreme Programming and Agile processes are economical feedback engines. They are a set of nested feedback loops that give you verification of every decision you make as soon as it's economically feasible. This is as much empirical process control rigor as you can get. Because of the overlap and how they backfill each other, it doesn't magnify the cost like the old ASE practices did. Agile set off with the right goal in mind, with appropriate disciplines and practices suited to our field. It's not the endgame, but it's a step down the right path.
What’s Next?
So what's next? We've gotten a lot better over the past 20 years at reliably building systems that do what customers expect. But frequently, our systems don't scale or have terrible security problems. We need to get better at capacity planning, security design, and "non-functional" requirements.
I think to make progress, we have to acknowledge the Platform-Application Divide. Some parts of our systems—operating systems, languages, frameworks—are part of the platform. It's important to get them right and make them fast. On top of those sit applications. We need different priorities for these two sides.
For Applications: We should stop using unsafe languages that allow null pointer exceptions and buffer overruns. We have a wealth of safe languages now that allow high abstraction and perform well. I have stopped using mutable state in applications until measurement shows me the need to optimize. I like languages that make immutability the path of least resistance.
For Platforms: You can't build a lot of platforms without breaking those rules. In that case, we should try to keep the platforms small and employ formal methods for verification. Formal methods are reaching the point where they are practical for proving the validity and security of small micro-kernels. We can use it for the platforms, and then use safe languages on top of them.
And finally, as every engineering discipline does, keep correcting our course with feedback. Look at what works, look at what doesn't work, think about how to do better next time, try it, write about it, and spread the word. As practitioners, we can drive the progress of our field.