I’ve been thinking a lot about how software engineers solve problems. Where I work, most of the time someone is given a problem (either by a lead, manager, or peer) and told to solve it. When you give a software engineer a problem to solve, most of the time they end up writing … you name it … software.

This is interesting because at first glance, this is working as expected. Why else did you hire software engineers? The answer might surprise you - software engineers should sometimes write software, but they should avoid it at all costs.

Let me walk you through the best to the worst possible outcomes for a hypothetical problem:

“the toaster keeps burning my toast”

  1. Do absolutely nothing.

    Yes, do nothing. It could be quite unsurprising that the job of a toaster is to heat toast, and that, in some cases, results in burning the toast. Sometimes you can solve a problem just by asking a question like “what would you like me to do about it” or “how is the toast burned”.

    Maybe the toaster was mis-configured, or maybe the person just wanted to vent a bit about the quality of the kitchen appliances in the employee lounge. Neither of those might need a solution, so ask.

  2. Tell them what to do different

    If you can tell a very small number of people, only once, to do something different, and you have high confidence they will do that thing, this is much lower cost. For example, if there is a single employee burning their toast, just show them how to make toast. There, you’re done.

  3. Write some documentation

    I’d argue writing documentation is a core tenant of software engineering, and is partially what seperates software development from engineering, but alas, others do not agree. In the meantime I take every opportunity to write documentation over software, because:

    a. Documentation is easier to change.

    b. Documentation can not directly cause bugs, crashes, memory leaks, or security issues.

    c. Documentation helps develop positive culture.

    So yes, if you can write a post-it note explaining that “past setting 4, the toast burns”, that is a quite cheap solution compared to writing software. Just don’t put the sticky note too close to the heating coils.

  4. Write a test

    At first this is confusing, because I just said we weren’t going to write software. What I meant to say is we are not going to write features, we’ll introduce a new test that will make sure the toaster cannot be heated past a certain temperature.

    This approach works best when the source of your problems are other engineers unintentionally breaking existing code, or downstream dependencies changing how your program behaves. By writing a test (and a good one, flaky tests are often worse than no test at all), you don’t require your users to do anything, they’ll get the benefits over-time (and better toast).

  5. Write a type

    As in literally, a type signature. A lot of problems in software engineering is that we deal with unvalidated state, often in the form of user input (but not always), and the easiest thing to do is to keep passing that unvalidated data around.

    Imagine we had a program that passed around temp: i32, or an argument for an integer (in Rust). That is technically sufficient, but eventually causes problems like: “what does a negative temperature mean, this is a toaster” and “why can we set the toaster to 1000 degeres”.

    By writing a type, say SafeTemperature, you can constrain (and validate) that only approved temperatures are flowing through your toaster. Simply change your parameters from i32 to SafeTemperature, and you’ll know the value is pre-vetted.

If all of that fails, maybe software can help, but I hope I convinced you that problems solved in any way other than writing software (or at least complete software systems) are the way to go. Over time it will become obvious that some docs are insufficient, and need to be types, and some type are insufficient, and need larger abstractions (and systems), but you’ll figure that out on the way.

Or at least, I hope I will.