What You Actually Need to Know About Software Development
The single most important thing to remember about software is that there is
Every behavior you see, every effect you run across, only happens because some code is making it happen, and it is always possible to track that code down.
That doesn’t mean it’s always useful to track it down. Sometimes all you need to know is why something happens and why it isn’t relevant. This leads us to:
Software is complex enough that we regularly create code that no human can hold in their head all at once. We work on things this complex by structuring them in smaller, more or less self-contained parts, with defined interfaces between the parts. That way, if you can fit the smaller part in your head, it’s safer to work on that chunk and trust the other pieces to be sane. (If this reminds you of microservices, there’s a reason for that!)
Getting even one of the smaller chunks into your head can be hard, but while it’s there, you can do things like just knowing the line you’re looking at is wrong and how to fix it.
This is possibly the most challenging thing to explain to people about software work – though most fields have something like this, it’s often not as dramatic as it is for us. Part of the reason for the difference is
Much of what we think of as “intuition” is really taking advantage of structures in our brains that have become highly optimized over a million years to help us manage the complexity of the physical world around us. But computing isn’t part of the physical world around us: my laptop creates its own extension of the world, and I have to go there to work.
The power of software engineering is that that extension of the world is so very, very malleable – and that power is amazing. But one of the curses is that since we’re no longer working in the physical world, our intuition no longer Just Works, which makes everything dramatically harder to understand unless we do extra work to enable our intuition to help us out.((A great example of this sort of enabling work: graphs are generally much easier on us humans than tables of numbers. We have to use the cognitive part of our brain to think about what the tables of numbers mean, but the graph engages a ton of pattern-recognition hardware in our visual cortex and we can grasp it instantly and intuitively. But the tables of numbers are easier to code… so guess which one gets used more often?))
One of the major things that breaks our intuition is that
Time is Weird
Computers these days are fast. Even your phone operates on a timescale a billion times faster than our brains: a second passing for us is kind of like 31 years, give or take, passing for it. That means that we can never watch what a computer is doing in its real time.
Instead, we’ve had to invent ways to force the machine to match our glacial pace, usually by asking it to wait for us, or to remember everything it’s doing and leave a log that we can read later. Of course, these techniques slow things down so much that any timing-related problems will usually go away, or at least look different.
Taken to the extreme, we end up flying blind, with no idea at all of why the code just went off the rails.((Or, often worse, no idea why the code miraculously did the right thing!)) To avoid landing in this situation, we have do real work to arrange for the code to explain its decisions to us. That work is often difficult and unsexy – but it’s critical, because of the major influence of
In computing, Murphy’s Law can be restated as “if you can’t prove that it’s impossible, it’s guaranteed to happen – often quickly.” Remember that billion-to-one speedup? If you do a billion operations a second, and each is 99.99% percent reliable, you’ll see 100 failures every second.
Our senses of probability and chance are rooted solidly in time: “this isn’t very likely” really means “this isn’t very likely during the time I’m thinking of.” Change the time scale radically and all of that changes.
Even if it didn’t, though, failures would still be a thing because of
1. Comment Your Brain
The purpose of comments in code is not to explain what the code does – I can read the code for that! No, the purpose of comments is to explain what you were thinking when you wrote the code.
Why do you think it’s important that the code do this? What has happened elsewhere to make it necessary? What effects should someone else watch out for after this happens? Is your code doing things in a way you like, or in a way that worries you? Why did you pick that way? What other ways could you have chosen? Might one of them be better? What were you thinking here?
Six months after writing something, you won’t remember the answers to any of the above, not even in your own code. Write it down. It’s priceless when you’re trying to figure out why something broke.
2. Test First
You can write code that tests to make sure your other code is working. This turns out to be amazingly important, and most of us suck at it because there’s always other stuff we could be doing. So write the tests first. It’s the only way to know they’ll get written.
3. Make Life Easy
The C language is a great example of this. It wasn’t designed to be easy to work in, it was designed to be possible to implement on the machines of 40 years ago. As such, writing in C means you spend all your time thinking about implementation mechanics, rather than about the problem you’re trying to solve: the how, not the what or the why. I stopped using C something like a decade ago. These days I write mostly in Python, which is much better about letting you think about what and why, rather than how.
This is where the purists jump in and say “But Python is slower than C!”((This is also the point where rabid fans of every language that isn’t Python jump up and start screaming. We’ll just take that as read and move on without wading into that debate, shall we? Python is definitely not a perfect language, and I’ll be writing more about languages later.)) And they’re absolutely right – but in 2016, that doesn’t matter for 95% of the world. The computer is so fast that it can soak up Python’s inefficiencies and still be fast enough most of the time. Languages like Python optimize for developer performance, not processor performance, and that is the correct tradeoff.
4. Solutions Beat Code
Writing code is easier than solving problems.
Code tends to have well-defined inputs and outputs, a predictable way to use it, and a time when you can say that you’ve finished the task of writing the code. Problems are messier. They involve people and organizations, and they have real-world constraints like getting people to actually use the thing you’ve built.
Solving problems is way more important than writing code. It’s easy to forget that, and important to remember it.
Finally, there’s one more thing that may be more important than all the rest combined:
5. Never Stop.
Things move quickly in software. You’ll never know it all. In fact, until the moment you’re ready to quit, you’ll never even be at a point where you can say you know enough. Keep learning, keep broadening your horizons, keep your eyes open.
There’s a place in this world for people who put down their heads and specialize in one little area, but in my experience there are more places for people who learn voraciously and work on being able to solve any problem that comes their way.
All told, being able to move your fingers and change the world means some serious brain stretching and a non-stop influx of new information. But for all that, it’s wicked cool.