I’ve done it, you’ve done it: when we’re talking to non-developers (aka, normal people), we sometimes use analogies to describe what we do. We use these analogies because they are helpful. Nevertheless, danger often arises when we infer too much about software development from our analogy.
When I worked for TurboPower and I had to explain what we did, I used to say that building software was like building a house. Yes, you could go out and chop down trees, cut the wood into planks and 2-by-4s, mix some clay and fire it into bricks, and so on, and then assemble them into your dream home, but in reality you don’t. You go to a wood merchant and buy pre-cut and planed wood. You go to the brick merchant and buy the bricks and mortar. So, I used to say, TurboPower is the brick merchant: people come to us and buy our bricks (that is, software libraries) and then build houses out of them. We had no control over what house they built or how they did it, and they chose our bricks because of their shape and color (design and API, if you like).
As an analogy it wasn’t perfect, but it got the idea across. But I’ve noticed something else as well: if writing an end-user application can be compared to building a house, then, the argument goes, we should use the same methodology in writing software as we do when building a house. We hire an architect to design the house by gathering our requirements for the house (bathroom next to the two spare bedrooms, outside faucet out back, room for a deck, Ethernet wiring, etc) and then to draw up the full design. The architect’s drawings then have to get approval from the local county planners, before the contractors arrive and start digging the foundations and taking coffee breaks.
That, I argue, is when the entire analogy breaks down. Building software is nothing like building a house. Why? Because software is (pretty much) infinitely malleable. Housing materials are not.
By creating the house design up front the architect can calculate loads, work out where pipes will go, balance the number of electrical outlets in a room, make sure doors can open fully, follow all the local house building rules, and all those fun things. Once the foundation is dug and laid, changing things can quickly get very expensive.
Software, on the other hand, gets expensive to change much less quickly and some changes will remain essentially free. (After all, we developers invented the notion of "feature-creep".) Yes, agreed, there is a point at which it’ll be too expensive to change some major foundational subsystem (example: after about 4 months into my last project, I began to wish I’d designed it to use smart clients), but you can tweak less major stuff ad nauseam. The user suddenly wants to track the hat size of their customers? No problem, we’ll add the extra column to this table here, tweak these stored procedures here to return it, add the new column to the business layer over there, and expose it as a field on this form just there. The cost and effort is minor compared to adding, say, another sink to an upstairs bathroom after that room has been tiled and finished.
Consider this also: if we break our software while testing it, it’s still there undamaged. Magic, eh? We can change it quickly and test it again. We can test all our design and implementation a dozen times an hour. On the other hand, testing a car’s resiliency to frontal crashes totals a car every time. Just imagine if we used a software-based approach to testing in designing a car ("let’s corrugate the chassis here and test it again." Bam! "Oh well, let’s make the corrugations less deep." Bam!), we’d bankrupt the automobile company in a day.
In other words, we shouldn’t approach the design and building of software like we do any other product. It’s not. It’s on another planet. It’s a different process altogether. If we treat software production as if it’s a traditional engineering discipline, we lose out on the great thing about software: it’s malleability and testability. Don’t take analogies too far.