Originally published on Simple Programmer. Cover image created by their talented team. Crappy hand sketch? All me.
Compilers. They are powerful tools that allow programmers to write extraordinary software using high level languages rather than having to mess around writing assembly code or worse: ones and zeros.
They are the beginning programmer’s most exacting teacher. They meticulously comb through every character of your code, searching for flaws to point out, but in the end, working with you to make your code run.
Compilers also provide a little bit of safety and comfort that some developers find hard to go without because they catch mistakes and warn about common problems long before you even begin to run your code.
My compilers do all of that too! Only, their names are Ken and Ed and Jesse.
I’m a mechanical engineer. That means that I’ve studied how forces, loads, impacts, and energy affect useful mechanical devices and systems. It does not mean that I can fix your car/refrigerator/AC/toilet/computer/dog. Actually, I’m pretty good at fixing toilets. My grandpa was a plumber. But that’s beside the point!
Mechanical engineers are in charge of designing all kinds of cool things: space shuttles, superchargers, heat exchangers, robots, washing machines, and more.
Personally, I design molds that get used in injection molding machines to make plastic medical parts like handles for surgical devices and fluidic chambers. Mechanical engineering is a pretty wide field, so there are a lot of specialities you could focus on learning.
Some mechanical engineers do work that’s super theoretical like doing all of the math and computing required to find out if the panels on the space shuttle will withstand the heat of re-entry.
Other engineers work on a lot more immediate, tangible things like designing parts with the right tolerances so that when all of the parts get made, all of the dimensions are right and things fit together and work like they are supposed to.
Ever tried to make a wooden box and everything came out crooked because one panel was just 1/16 inches too big? A lot of the molds I design wouldn’t work at all if things were even .005 inches different from the plans!
However, just coming up with a design for how things should look is usually the easy part. Everything seems so simple in my 3D design software when I can just wave my magic computer-aided design (CAD) wand and things look the way I want them to.
The real hard part is figuring out how each and every piece is going to get made. When you are designing things, it’s easy to hand-wave certain details away and say “We’ll figure that out later.” But when you go to machine them, there is no later. You have to have everything nailed down. Every. Single. Detail. Because, in a real, finished design, there’s no way to say “And these pieces will just be attached together somehow.” OK. How? When? By whom? So in order to figure out how all these little details will work, I am constantly leaning on the expertise of our machinists.
This process is going to vary pretty widely from company to company. Some companies where teams are larger or there is more regulation will have more strict design review and validation procedures. On smaller teams and in less critical applications, there may be a slightly more freeform process. This is no different than the software industry.
The “compiling” process starts as you finish up a design and send it out to the shop for machinists to take a look at it. They start looking at things like:
What kind of material is it? What shapes should the base material be? Round rod? Flat, rectangular plate? Thicker, square blocks? This is similar to a software compiler deciding what memory it needs to allocate.
How are the machinists going to set up the material and hold it steady while they machine it? A vise? Double-sided, super-sticky tape? Clamps? This is just like when a software compiler has to decide what platform it is targeting.
How will they machine each feature? What order will they do it in? That makes a difference because machining always leaves little thin wisps of metal called burrs in some direction. Having burrs in some places is much better and easier to clean up than others.
In software terms, what “optimizations” can they perform, given my design? Are there little, unobtrusive features they can add that will speed up the build? A software compiler might unroll loops or inline constants so they don’t have to be looked up at runtime.
What size and shape of tools (often, end-mills that look a lot like fancy drill bits) will they use? Bigger tools will be more stable and easier to cut with, but sometimes, you can’t fit larger tools into smaller areas. Will they use a tool with a flat bottom, one with a full round, or something in between? This is when a compiler figures out what libraries are available to it.
As they look at the part, they’re trying to come up with one single unified strategy that answers all of the questions above and does it in a way that allows them to work smarter, make minimal mistakes, and take less time.
Often, they’ll get part of the way through formulating a strategy, and they’ll stumble on one of the “compiler errors” we’ll talk about in a minute, and it will blow their whole strategy up.
A lot of times, they’ll come back to me, and they’ll say, “I was planning on making it out of ½-inch rod, holding it in the vise, and cutting the whole thing with a 1/16-inch ball end-mill, except this one feature makes that impossible.” This is when I have to slam my head on my desk a little, go back to my design, and figure out some tweaks that will make my “compiler” happy.
Can you see the similarities between the processes the two “compilers,” software and human, go through? Find resources, set up tools and libraries, set up and discover the environment, plan out a strategy, optimizing along the way, and then go to work, making the engineer’s vision come to (hopefully) functional life!
A lot of times, a design might look nice and clean from a distance.
But our machinists have to machine each and every feature of this part, and that means that they’ll have to inspect, lay out, and plan their approach to making each of those features. As they do that, they might notice something:
Gasp! A sharp corner that we can’t machine with a round spinning tool.
Think about it. The tool is round and spinning and cutting metal away all around the edge of its circular boundary. There’s no way we could cut sharp corners like that for the same reason that it would be extremely difficult for you to draw a sharp corner in the sand with your finger (well, unless you have one of these.
When something like this comes up, my machinists will sigh heavily, bring me out to the shop, point at the sharp corner, and say, “How exactly do you propose to do that?”
The code might look fine from a high level as you read through it quickly, but one little finger-slip will leave you hunting desperately for that extra parenthesis as your compiler stoically reminds you that you’ve got a syntax error somewhere, and it can’t compile.
One thing that surprised me (and maybe shouldn’t have) when I first started working as an engineer is that you can never really machine something exactly to the number you are supposed to. That’s not how decimal numbers work!
Measure something. Anything. How big is it? ¼ inch? Is it really? Or is it just a touch bigger than that? Maybe .251 inches? Maybe .250001 inches? How can you be sure? Can you measure to the nearest .000001 inches? Some people can!
At a certain point, the discussion gets silly, and every engineer learns that there is some point where it is “good enough.” But what is that point? Well, that’s called a tolerance, and it’s our job as engineers to figure out what the tolerance should be! What is the absolute biggest this hole can be before my assembly doesn’t work any more? What about the absolute smallest?
Now, instead of a single number measurement, you’ve got a range. Maybe it can be between .245 and .255 inches. A tolerance of +/- .005 inches is actually pretty standard in the machining world, and it blows my mind every day that we can expect to make something accurately to within about the thickness of a piece of paper.
A common error is that some engineers will design parts but forget to allow extra tolerance where things are less important. They’ll ask for +/- .001 inches on every single part of their design. It’s doable, but the difference in time and cost can be measured in days when you have to meet that specification!
Is the code specifying what is actually needed? Could it require something more general to make life easier? Are major features of the build forced to jump through hoops because of this careless over-specific requirement? Are you satisfying these self-inflicted restrictions all throughout the code? If not, you’re going to see a type error.
Sometimes your pieces work fine individually and can be machined without any problems, and you don’t find your mistake until you go to assemble everything together.
Whoopsie daisy! That’s not going to work; those holes don’t line up. Depending on what’s happening, these errors can be the most insidious and hard to find. Until it’s too late. They can have the biggest effect on the build and cause the most customer angst.
The individual pieces seem well-designed, and there are no obvious flaws on either one, so the compiler happily compiles them. However, when it comes time to use them, suddenly the problem appears, and the assembly can’t function.
This is analogous to a runtime error. It seems to compile OK, but it crashes at runtime because the pieces don’t quite fit together. Because, at this point, the problem is a lot closer to the customer, they are more likely to be directly affected by this type of error.
This isn’t necessarily a compiler error per se, but sometimes there’s a sneaky .0001-inch inconsistent edge or facet in your geometry because somebody got a little too crazy with their swoopy surfaces (looking at you, Jerry).
Everything will look fine to me and the machinists, but it causes little “bugs” in the code that tells the machine what to do. These bugs don’t rear their ugly heads until you’re up and running.
This can be slightly more destructive (and terrifying!) than the typical terse but intimidating software version:
However, the fundamental parallel still holds. The code looks good. The compiler is happy. The program runs fine … until it hits a sneaky memory issue and breaks hard.
And you, the programmer, don’t know exactly what went wrong. You know what class of error you have. Someone’s going somewhere they shouldn’t. But it doesn’t tell you how to fix it. It just tells you it was bad.
Anytime I’m reading a blog post or article about software engineering practices, discussions on good design, or a sleep-deprived rant about exacting compilers, it makes me happy to see how even though software engineering and mechanical engineering seem so different, there are so many similarities between the two.
We are all trying to solve problems for people that need our help, and we’re trying to do it in the most ergonomic, efficient, and flexible way possible so that the next generation of engineers can build on what we’ve done—rather than throwing the whole thing out and starting over. And we do it with tools and teammates that make our lives and our product better.