When considering other engineering disciplines, engineers rarely build the product. In electrical engineering, the product design is specified in ECAD, exported, and then assembled by a mix of robots and technicians. Similarly, mechanical engineering specifies the product design in CAD, exports it to a set of drawings, and then it is built and assembled by technicians.
Software, however, does not have technicians. There is no separation between the specification of the product design by the engineer and the actual product that is used by the end-user (At least, there is no additional role between the specification and the end result, because arguably the compiler or interpreter acts as the technician and turns the code into bytes used by the computer). This is fantastic, because it cuts down on costs and the lead time of products. But, I believe it has had a profound, negative effect on software engineering as a whole.
Unlike most other engineering disciplines, software engineering is complete and utter garbage. This is sadly not a controversial opinion. What is up for debate is why software is just so bad. I believe the sorry state of software engineering is not due to how young it is, or how different it is from other engineering disciplines, but almost entirely due to the missing technician role. The existence of this role forces the engineer to properly specify the parameters of the product, otherwise it is built improperly or it is outright rejected. If an electrical engineer takes hundreds of tries tweaking a PCB specification, then they are a terrible engineer and would be promptly fired. Yet software has completely normalized this way of working.
Clearly it hasn't all collapsed (yet), so obviously something about software works. We can easily see why by looking at one of the other, young engineering disciplines: electrical engineering.
The Breadboard, ECAD, and the Engineer🔗
I was originally an electrical engineer. Starting out, I learned how to use a breadboard. A breadboard is a brilliant and simple piece of technology. It consists of a grid of holes where each row is connected together electrically. It makes experimenting with LEDs, diodes, switches and batteries easy and effortless. With an Arduino you can bring a microcontroller into the mix and build exciting new things.
As with everything, sometimes the circuit that you've built on the breadboard doesn't work. Thankfully, it is trivial to add a wire or two and hook up an oscilloscope or multimeter and start probing various connections. Sometimes the issue is as simple as a diode being backwards, which can be quickly rectified. Sometimes it is something more complicated, but everything is exposed and easily checked so you can trace it down.
Breadboards are easy to get started with, are extremely flexible, and can be effortless inspected with a variety of tools. But they also have downsides. They are big; wires can come loose or just be in the wrong spot; they are time consuming to recreate; and sometimes they are just a rats nest of wires and impossible to debug.
Eventually I was required to learn an ECAD system. Specifically, I had to learn Altium. This is a horrendously complicated piece of software which is used to design PCBs. Instead of having a simple grid that I could plug things into, I now had to design a schematic and then design a PCB layout. Starting out, it constantly yelled at me for unconnected nets and other issues -- things I didn't care about. When routing the PCB layout it would argue with all of the traces I made and there were constant red errors until I did seemingly arbitrary things until they turned green. I didn't understand what the tool was doing, and so I hated it.
Even after I stopped getting cryptic red errors and I was ready to export my PCB design and send it off to get fabricated, I had to wait 6 weeks for it to get to me! Once the PCB arrived the "fun" just continued. The damn thing didn't work. Except now I couldn't just plug in some extra wires and hook it up to an oscilloscope. I had to try to find an exposed surface connected to the thing I wanted to probe, and then I had to solder a thin wire to it. Sometimes, there is simply nothing exposed and you are shit out of luck. And once the error was found, it wasn't as simple as unplugging the misbehaving wire, you now had to use an exacto knife and cut the PCB trace and then solder tiny wires all over the place to fix everything.
My first experience with ECAD I just wanted to get back to a breadboard. It was faster to develop, easier to debug and just simpler to understand. Thankfully my mentors in life refused to allow me to sit in this local maximum, and I was pushed to use and learn the various ECAD tools. Curiously enough, I did not just learn to love ECAD -- I learned to hate breadboards. Where once breadboards gave me the power to wield electricity, they now hold me back and frustrate me endlessly.
Local Maximums and Forcing Functions🔗
How did this transition happen? How did I go from being empowered by breadboards to being held back by them? The observant reader will notice that many of the descriptions of a breadboard map to duck-typed, interpreted programming languages, like Python or Ruby. And, interestingly enough, I have an almost identical relationship with Python as I do breadboards! The reason is that both of these tools are local maximums. They provide endless flexibility, easy debugging and rapid iteration, but are perhaps less reliable or as fast as some of their alternatives.
But first, what does this have to do with technicians? In electrical engineering, the existence of technicians is a forcing function for learning proper ECAD tools. If you gave a technician a wired-up breadboard and said you needed 100 copies, they would likely tell you to fuck off. Software engineering has no such forcing function, and so we have a significant portion (perhaps the majority) of the industry in a local maximum using the equivalent of breadboards. The lack of a forcing function to use better tools has caused our industry to get stuck.
But why? I stopped using breadboards when I realized that the errors I used to make, and find easy to correct with a breadboard, I simply no longer made. And the errors that I now make, a breadboard has no way of preventing (and in general make so much harder to correct). For instance, accidentally connecting the wrong wire to the wrong chip or pin is not a thing I commonly do now. I always double check my schemas and my datasheets, and for new devices that I haven't worked with yet I build a breakout board to learn the constraints (and specifically, where the datasheet is wrong). Sure, I sometimes need to fix traces and solder some rewiring, but this is no longer due to silly accidents -- they are usually due to logic errors or an errata in the datasheet that I missed. In fact, the logic errors are largely avoided due to the schematic capture laying out the logic more accurately and easy to evaluate compared to a breadboard layout with its mess of wires.
However, the real reason I don't use breadboards anymore (or extremely rarely) is largely due to the lack of help they give with the things I now do. Taking an existing design and adding components or swapping out components is trivial in ECAD. I can just fix the schema, and then use push-shove routing to ensure that the component fits where I need it to. The ECAD tool ensures that my pre-existing traces are valid, and that constraints are being held (such as trace distancing). On a breadboard I have no such help. Swapping out a component on a breadboard is an absolutely miserable experience, since there is nothing checking whether you screwed up your other wiring as you did an invasive operation on the circuitry. Once I reached a level where I was no longer making beginner mistakes, I needed a tool that would help me with advanced mistakes, and I stopped caring about the beginner mistakes.
Now, notice how similar this is to programming languages. Languages with powerful, static type systems like Haskell make it trivial to rework a project. You just rip out what you used to have, put in the new code and then fix the type errors until it type-checks. This is unheard of in a language like Python or Ruby. And the tools that these dynamic languages provide become less and less useful in the first place. I no longer need the extreme inspectability of my running code -- I am confident that a for loop is going to act like a for loop, and that a Foo type is going to act like a Foo type. The additional flexibility that dynamic languages provide is now a downside -- I'd rather the logic be easily reviewed and checked rather than being able to plug in any type into any function.
The sad reality is that without technicians in software we have no forcing function to use proper engineering tools. This has had a profound effect on our industry -- and it would be identical to a large contingent of electrical engineers building shitty products with breadboards. Or mechanical engineers designing cars in TinkerCAD and 3D printing them.
Now, this is not saying that languages like Python or Ruby are bad, are going away anytime soon or can't produce great things. The Arduino community has shown just the number of amazing things that can be done with breadboards and an easy to use MCU. But you would be pissed if you paid a thousand dollars for a computer made with breadboards. Why do we tolerate the prevalence of people using ineffective tools in software engineering? Software engineering will not get better until we do our engineering with proper engineering tools. And that means strong, static typing.
Looking at other engineering disciplines it is clear that languages like Haskell, OCaml or Rust are the current proper engineering tools of software. They are hard to learn, provide a ton of tools to strictly specify your logic and provide constraints, and are frankly a huge pain in the ass at first. And oftentimes the main complaints about these languages are identical to the complaints I originally had about ECAD and PCBs: It takes too long to learn; it is slow to compile (its not even six weeks!); I don't understand why I need all of this.
So how will software engineering evolve without the technician as the forcing function? I'm hopeful that one day it will no longer be considered acceptable for an engineer to build a massive codebase using Python or Ruby, just as it is not considered acceptable for an engineer to build a massive circuit on a breadboard. And given the uptake in things like Typescript or typing in Python, it does seem that we're moving in the right direction. Just very slowly.