Skip to content

Engineering and Craft

Published: 2022-05-22

On having standards, building things in a creative way and writing code for fun.

I’d like to be a software engineer, although I struggle with the term.

It’s very difficult to think of myself or many of my newly found peers in the software industry as engineers. Writing code often feels like an adventure: only sometimes do I have a faint clue what I’m doing or where I’m going.

Looking around, it seems like I’m not alone: things break on a regular basis everywhere, from smart lights to airplane controls to elevators. Lines of code glitch and fall apart mid-process, sometimes with terrible consequences. We’ve come to expect it.

I’m just happy that when my experiments fail, nobody gets buried in a pile of rubble.

Yeah, but what do you know about anything?

Fair, I’m new to this. I started coding barely two years ago and have been doing so professionally just over a year. So here’s someone significantly more experienced than me:

We should recognize and accept that our discipline is a creative design discipline and has no meaningful relationship to production-engineering and instead focus on mastery of the skills of exploration, discovery, and learning.

David Farley, Modern Software Engineering

That’s Dave Farley, in a recent book of his, on the topic of software engineering. He’s an old-school professional whose thoughts are worth a ponder.

Titles and credentialism

The word engineering implies a fairly rigorous process and a high bar of competence. Not all who program are engineers in this sense.

Titles in the software industry seem to most reliably indicate salary, rather than difficulty of the task or burden of responsibility. The success of software startups is more about stumbling into a market than building quality software.

“Software engineers/data scientists are paid more because we have to study more to keep up to date with this fast-moving industry that changes more than any other field.”

Sorry, but this is false: both that we need to study more, or that this is the reason.

Gergely Orosz in a tweet on May 10, 2022

Electrical engineers or construction engineers accept liability when things go wrong. By signing off on a document, an engineer takes personal and professional responsibility for the accuracy of their best effort. It’s more than credentialism, at least that part of it.

In software, a seal of quality accompanied by such responsibility seems rare.

Margaret Hamilton was a software engineer. That’s the standard.

Should developers calling themselves engineers make more of an effort to earn the title? If so, what would that effort look like and what are the metrics?

What to measure?

There are signals that stability and throughput are good signals for a software-engineering firm or a team to follow.

Work done by Nicole Fosgren, Jez Humble, and Gene Kim in the ”State of DevOps” reports and their book Accelerate suggests there’s a statistically significant correlation on good outcomes and these two measures. Doing well on these metrics seems to coexist with an organization making more money, for example.

Here’s a short summary of those findings:

.
├── Stability
│  ├── Change failure rate
│  └── Recovery failure time
└── Throughput
   ├── Deployment frequency
   └── Lead time

Stability is tracked by two metrics. The first one is the change failure rate, or what percentage of changes introduces a defect, such as an impaired service. How often stuff breaks. The second sub-metric of stability is recovery failure time — how long does it take to recover from an unplanned outage or failure.

Throughput can be distilled to two metrics as well. The lead time, or how long does it take to get from idea to working software. Deployment frequency tracks often the team deployes changes to production systems, allowing for quicker iteration and, presumably, learning.

Simplify

Managing complexity is a core skill for a software engineer.

The way I most immediately relate to this is by taking a quick glance of the source code of almost any modern JavaScript app. The endless subfolders buried under node_modules, hundreds of little bundles of code, contributing to the functionality of the app in different ways. Much too numerous for any one person to keep in their working memory at any given time, mostly kept hidden while working on day-to-day code.

It’s great, useful and dangerous.

The dangers of complexity are best articulated by Jonathan Blow, a game designer who created Braid, in his brilliant talk from 2019, Preventing the Collapse of Civilization.

The whole video is very much worth a watch, but in short, here are some consequences of growing complexity in software:

  • Deep knowledge gets replaced by trivia
  • A person can know a smaller percentage of what there is to know
  • Good information gets drowned by noise.

There will be knowledge loss at each generational transfer, which leads to a decay in skills needed to innovate and maybe even maintain existing systems. In the worst case, this leads to a loss of capability for humankind, with many such precedents in history, ranging from Byzantine flamethrowers to ancient Greek analogue computers to 1600-year-old nanotechnology.

Continuing on the same theme, here’s Samo Burja, a sociologist who founded a political risk consulting firm, talking about knowledge transfer:

A solution to fight this decay over time in software could be to simplify at every level: hardware, operating systems, application code, network connections, compilers, user interfaces and so on. Luckily there are also tools and techniques that help with the goal of managing complexity, such as modularity, abstraction and separation of concerns.

Yet there’s also an important difference between simplifying and building more layers of abstraction. Higher abstraction simply hides complexity and can in fact add to it. Working on higher levels of abstraction may be justified and even necessary, not least in order to give more people a chance to leverage code.

It’s nevertheless a useful conceptual difference to make between simplifying and abstracting. Lest we lose the capability to improve technology.

Creative precision

I consider myself a craftsman, in the sense that things I make are mostly unique. Not particularly valuable, just imperfect in varying ways.

I can cook, but I wouldn’t trust myself to feed more than a few dozen people. Similarly, I can whip up a working web app, but I probably could not write code that’ll land a spacecraft on the moon safely every time.

What’s missing here is precision and scalability. Sometimes I get it right, sometimes I won’t. That doesn’t seem like engineering.

An idea of a recipe or an experience, some mise en place, some stuff made from scratch, some familiar techniques and patterns. Some completely new stuff tried out for the first time. There’s a beauty to home cooking.

Still, it’s not a particularly suitable approach to creating durable software that minimizes complexity and can be worked on by many people simultaneously.

Practical learning

The creative approach to building software is by necessity: often we have no clue what our users want — and they don’t either. It’s best to assume we won’t get the design right the first time, or catch all the bugs and think of all the edge cases. The illusion we could plan for all of that didn’t survive contact with reality.

Working in iterative steps, correcting course through fast feedback loops should be a priority. That requires mitigating the cost of mistakes, of which we’re guaranteed to make many.

It’s a very practical way of learning.

Opposite of a good idea

This kind of iterative, creative approach reminded me of a non-technical writer I’ve enjoyed: Rory Sutherland, who made his career in advertising. Sutherland talks about the perils of trying to make sense of everything with verbalized logic. Sometimes our rationalizations have nothing to do with the real reasons we act a certain way.

Logic may be a good way to defend and explain a decision, but it is not always a good way to arrive at one.

Rory Sutherland, Alchemy

The opposite of a good idea can also be a good idea, he says. If it’s cheap and easy to test that out in your application, why not do it?

It’s possible to judge whether something works before understanding why it works. One may stumble upon useful insights when unburdened by rigid plans. This is especially true when people are involved. A good guess that withstands empirical observation can be a scientific breakthrough.

There’s also the question of whether an engineer is simply expected to complete assigned tickets, or leverage one’s coding skills more broadly. Solving problems and making decisions directly impacting the business itself, or another goal that people are cooperating to accomplish. Being involved in a more holistic way with a firm’s success is likely more common along the top end of the compensation scale.

Staying true to the iterative, creative learning process of developing software, yet setting clear standards and measuring them. That seems like a worthy goal: reproducible magic.

Topics:careerreflections
CC BY-NC 4.0 Kasper Viita