Watch Your Input/Output Ratio

Every now and then I remember something from teaching high school that is incredibly helpful to learning to program, and today is one of those occasions.

If your learning is all input and no output, you will develop a backlog of information that has never been processed, and stop retaining it. In order for long term learning, the rate at which information goes into your brain and the rate at which you do stuff with that information need to be comparable. Maybe not the same, but certainly within an order of magnitude. Here’s a simple example:

Suppose you’re trying to learn Spanish. You come to my class every day, and I talk to you for an hour about Spanish grammar. After one week, how much Spanish can you speak? Basically none, unless you’re either an experienced or naturally talented language student, or you already knew Spanish. Now suppose you come to my class, and on I teach you four sentences to say, and then give you some time to practice. The next day, we practice those four sayings, I add two more, and there’s more practice time. At the end of the week, you’ll probably be able to count a bit, introduce yourself, and ask where the bathroom is.

I think most readers have been through enough good and bad teaching to recognize the two situations, and to see which one is preferable. In the second situation you learn more because you got practice. Your input/output ratio had enough output that you learned to do a thing. So, on the one hand, if you read programming blogs all day and don’t write code, close the browser and go write some code.

But what if your input/output ratio is skewed in the other direction? What if, for example, you spend 8 hours a day writing code and never read code, or read books about code, or read blogs about code? Let’s go back to Spanish class.

Imagine, halfway through the semester, that your teacher quits explaining new phrases or sentences. Students continue to practice. Some of the clever ones learn to recombine pieces of the sentences they know, and continue to progress for a time, and some of the dedicated ones find a few key snippets on SpanishOverflow, and begin using these, but no one really gets better without input.

How can you gauge your input/output ratio? Well, unfortunately I can’t just tell you, “oh, about 5 to 7.” You have to pay attention to yourself, to how you’re reacting to information. So here are some symptoms that I would look for, when teaching, to gauge the input/output ratio of my classes. (Of course, “input/output ratio” is not what an education textbook would call this, but this is a programming blog, and “output” sounds way cooler than “opportunity to practice what you learned.”)

The Symptoms

Symptoms of too much output (that is, symptoms of not enough input):

  1. You feel like you’re repeating yourself. You implement a feature, and when you’re done, you could swear you wrote that same code last month.
  2. You’re bored – implementing features, hunting for bugs in the code base has started to feel routine.
  3. You have no mental backlog. There are no side project ideas, cool language and/or language features you wished you got to use, interesting podcasts you’ve listened to, security vulnerabilities you just heard of, etc. The list of stuff you’ve heard of but haven’t tried is empty, or low. That is, you’re writing code from an empty tank. There’s nothing you’re itching to sit down and try. (Be careful with this last one. If it keeps up long enough, you’ll start to be unhappy and lose productivity really fast.)

Symptoms of too much input

  1. You have a case of Object Happiness (as observed by CodingHorror).
  2. You often go to use something you learned recently, and you discover that you don’t remember it well enough to get started without going back to the source you learned it from. For example, when I started learning R, I was watching a lot of videos about R, but I never got a chance to use it at work, and I never made time to use it in side projects. Then, when I went to implement something in R, I was right back on StackOverflow because I could only half remember any of the R videos. I needed to make time to practice and slow down on the videos. 
  3. You’re in the middle of reading 5 books but don’t have a side project.

Which one is More Common?

I think that, by and large, too much output is more common than too much input. After all, professional developers get paid to write code, not read books about code. Even if a lot of the time we spend “writing” code is actually spent reading code and deciding what code to write, we still spend a lot of time expressing techniques and idioms that we know, compared to the amount of time we spend acquiring new ones.

On the other hand, I think fresh out of school (or freshly self-taught) developers might suffer from too much input. That’s why Object Happiness is associated with inexperience. I think, frankly, that if you made experienced developers take a three year training detour in the middle of their lives, they would come back with too much input and would try to bake piles of extra technique into every line of code.

The Cure

Of course, the cure for a ratio being out of whack is super simple: Add stuff to the side that’s too small. If you feel like you have the “too much output” symptoms described above, try picking up a book or watching some training videos. If you have too much input, especially if you’re a student or are in the self-teaching phase of your software career, get out and write some code.

Till next week, happy learning,

Will

What’s a Bug?

What is a bug, anyway?

The answer depends a great deal on who’s asking:

The User

If the user reports a bug, they basically mean, “I was using the software and something happened that I wish was different.” This could mean that the “delete” button works perfectly, but that they didn’t mean to press it. Or it could mean that the back button in a SPA doesn’t revert to what they thought was the previous view. Or it could actually represent a flaw in the code. But these situations look similar to users, especially non-technical users.

A bug here is basically a discrepancy between the user’s mental model of how the app should act and how the app acts. (And remember, as far as your user is concerned, your app is your user experience.)

The Dev:

As a developer, when I think about bugs, I tend to think of them as code first. There is something intrinsically flawed in the code. The flaw might only be exposed in one narrow set of circumstances, and therefore be hard to find. But code is, in some way, actually wrong. These could be something silly, like an off by one bug in an array index, or downright hard to find, like a rare race condition in some cache refresh. But whatever the difficulty of the bug, it’s a code flaw.

The specifier

By “specifier” I mean anyone who helps design the software. It could the a program manager, a client of a consultant, a project lead, another dev. This perspective on bugs has three distinct subspecies:

  1. The code does not implement the specification
  2. The specification does not meet the specifiers expectations.
  3. There is behavior that was never specified, but turns out to be relevant.

Case 2 is very similar to a user bug. The code doesn’t do what I thought it would. Case 1 is probably the only clear cut instance in this article of a mistake by the developer.

Case 3 can come in a variety of flavors. One example is a “What about time zones” kind of bug: your code checks to see whether two events occur on the same day, but forgets to check which time zone the two events were in. Everything is fine until, suddenly, the Hawaii office starts post-dating checks when they work late. We’re in case 3 because no one said what should happen if the two events are in different time zones, but then in real life it came up.

Who’s Right?

If they developer faithfully implemented the specification, but the specification didn’t say anything about what the back button on the browser would do if you pressed it on the receipt page, so the developer didn’t make any effort to control that behavior, and now the user wants that back button to return them to the last item they added to their cart, no one’s happy, and no one’s really in the wrong. So who’s right?

No one, really. There isn’t some Platonic form of ideal software floating around that we failed to implement. There also isn’t some user interface that will perfectly protect people from accidentally doing the wrong thing. There are, however, some guideposts:

  1. It should be easier to do the more usual, safer, more normal tasks. For example, in a photo app it should be easier to view the photo than to edit it, easier to edit it than publish it, and easier to publish it than delete it. (At least, if your client base is careful about what photos they publish.)
  2. It helps to get second opinions early in the process. If the specifier or a user acceptance tester doesn’t like the way an app behaves, decide what needs to change. If no one sees how the user interface looks until the app is almost done, people will be surprised and unhappy.
  3. Don’t take it personally – the goal is that your software should be fun and easy to use, not that you should be right about disputes.
  4. Compromise – there are tradeoffs in every discipline. If you want to have a button for X, you can now no longer have a user interface with only 3 buttons. If you want the button to be blue, it can’t be green. Iteratively move towards the best compromise.

Remember, software can’t be perfectly specified without being written. If I’m ordering bolts, then by the time I specify the expected strength and tolerances, the length, the thread pitch, the diameter, etc, I can exhaustively specify the bolt. Any order of bolts that arrives not meeting my specifications is clearly wrong.

Software is not like that. If I order an iPhone app, I can’t specify the behavior exhaustively in every case. A specification so exact that it can be mechanically implemented without human decision making is the source code, not the documentation. I think that’s why waterfall doesn’t work with software as well as it works with, for example, bicycle manufacture.

With bicycle manufacturer, I could specify all the materials and assembly processes for the bicycle, get them working, and then regard any deviation from this spec as a flaw in the bike produced. The upfront spec is king.

Imagine for a moment that you’re a bicycle manufacturer, and you’ve just gotten your designs set up with an assembly line and bikes are flying out the door. Your phone rings, and your panicked customer asks, “these bikes sync with Outlook, right?” The upfront spec cannot be king because software cannot be specified exhaustively except by writing it.

In an analogy to manufacturing, the source code is the engineering spec and the compiler is the manufacturing process. And we have pretty thoroughly debugged compilers at this point. 

I think where the process does need some refinement is getting the compiler’s output into the hands of the customer. If we want to stretch our manufacturing metaphor a little farther, that’s where shipping logistics comes in. For an interesting account of that, you should read The Phoenix Project. But the software, the source code itself, is always subject to change. Being ready and able to change it when you realize that the spec is wrong, or that the spec is right but the user doesn’t like it, is key.

Till next week, happy learning!

-Will

 

 

Contradictory Advice on Self-Sufficiency

At hack.summit() I heard a talk by Qi Lu, a researcher at Microsoft, who said, basically, “there are two kinds of developers. The first kind, you give them a task, and they disappear for half an hour, and then they come back with some question about it. The second kind, you give them a task, and they disappear for two weeks and then come back and ask what to do next.” Qi Lu then advises us to be the second kind.

On the other hand, Jeff Atwood writes,

When interviewing candidates for programming positions, I always look for someone who is brave enough to say “I don’t know” when they need to. Candidates who can’t or won’t do this get red flagged; those types of programmers are dangerous. “Can-do” attitudes have a superficial allure, but they’re actually poison in our field.

So what do I do? Suppose I am Schrodinger’s programmer, about to become programmer 1 or programmer 2. I have just left the boss’s office, and after an hour or two of writing code, I’ve hit my first real obstacle. Do I go back, admit that I don’t know, please Jeff but anger Qi? Or do I bury myself in code for two weeks, and at the end of it hopefully be programmer type 2?

I think these two pieces of advice only appear to contradict; the authors are pushing us towards a golden mean from opposite directions.

Qi Lu is running a research department, so his employees are answering questions that no one else knows the answer to anyway. I think Mr. Lu is not criticizing people who won’t admit they’re wrong, or criticizing people who work in a team; he’s criticizing people who whine that the work is hard instead of doing the work. This criticism is especially fair in a research department, because there’s no reason to expect that the programmer down the hall knows the answer; struggling through uncharted territory is the job.

I also think Jeff is not giving a free pass to people who don’t know anything or who whine instead of working; I think instead he’s stating that teamwork and humility are super important to building good software, and if you’re interviewing a candidate who’s too insecure or too arrogant to admit they don’t know everything, that is a serious red flag to future teamwork.

I think the real lesson here is this: if the best way forward is to get some help from a colleague, then get the help! If the best way forward is to slog through, slog through. The two things you don’t do are: (1) ask for help as a way of procrastinating when you really should figure it out on your own, or (2) refrain from asking for help out of shyness or nerves or whatever, when getting a little help would really be the best path forward.

On the other hand, if we did take both these pieces of advice really at face value, and Qi Lu were really telling us not to ask for help, I have to side with Jeff. It’s been said often that great software is built by great teams, not great programmers, and I’m inclined to agree. Few things are more helpful than a second set of eyes or a second opinion, and I wouldn’t want to work somewhere that discouraged asking questions.

This situation (that is, deciding whether to get help) seems to be also a place to figure out what’s normal on the team you work with, and go from there. Maybe deciding whether to ask for help (which is something you should expect to do fairly often when you’re new to this job) is a matter of both the kind of project and the kind of team. For example, I’ve heard senior devs complain that they can never finish a pomodoro for all the new people asking for help, and I’ve heard younger devs be asked not to waste all day spinning their wheels again.

Till next week, happy learning!

-Will

Three Great Podcasts

In an earlier post, I suggested podcasts as a good way to acquire knowledge during down time, but the knowledge acquired is different from that acquired during coding or study. I don’t expect to learn a lot of syntax from podcasts, for example. If a show starts saying “new line, public class Foo, new line, open curly brace…” I’ll probably stop listening. But there are a few things to learn from podcasts that really help:

  1. The existence of language features:  C# 6.0 has string interpolation? I won’t learn the syntax from the podcast, but I did learn that I should go look up the syntax.
  2. Names for things: Listening to a podcast (as long as you’re not driving) is a great time to look up terms you don’t know.
  3. Who people are: did some brilliant programmer get interviewed on a podcast? Maybe you can go to his or her blog. Maybe there’s a Github project you can go to and actually read code that this interview-worthy person wrote.
  4. What kinds of problems other people are solving: Somebody built a server that lets you monitor performance of web apps? I guess that means monitoring performance of web apps is a thing people do. Now I know. Troy Hunt is yelling at us to prevent sql injection for the 1000th time? I guess that’s a thing to worry about.

Software development is an iterative field. We build on other people’s successes, and learn from their mistakes. Very often, a new technique or new language is built to fix specific problems with existing technology. Hearing about new technology is therefore particularly instructive. Continue reading “Three Great Podcasts”

Know the Words: State Machine

Hello again, World.

Today we’ll be doing two things. First, we’ll be learning what computer science people mean by state machine, and second we’ll be picking up with our implementation of a Zork interpreter in C#. (I am, with increasing distance, following Eric Lippert’s discussion of the Zork Machine in OCaml.) My code is here on Github. We’ll see in a moment why these topics go together. (Also, please note that state machine is an official, talked-about-in-grad-school kind of term, and my explanation is certain to fall a little short; this is a beginner’s blog, and I’m a bit of a beginner myself, so the goal is to get everyone to understand the term, not get everyone ready to defend a thesis on it.)

Basically, a state machine is an abstract way of talking about machines that store some state. A state machine has a state and an input, and uses these to calculate the next state. We can think of a computer as a machine that has state (what’s in the memory now) and inputs (commands from the user) that calculates a new state (change to the memory) based on the inputs. [Aside: this explains why restarting computers can be helpful. When the computer turns on, a series of predefined commands are executed, designed to get the computer into a state that is generally usable. If we have somehow gotten our computer into an unusable state, turning it off and letting this predefined steps can get it back into a usable state without our having to figure out what went wrong.]

On my Android phone, I use SwiftKey, and I love it. SwiftKey (or, if you prefer, a mechancial typewriter) can be understood as a state machine. We have basically three states: normal, the shift key has been pressed, and the caps lock key has been pressed. If you press ‘a’ in normal, you get a lowercase ‘a’. If you press ‘a’ in the shifted state, you get an uppercase ‘A’, and state transitions back to normal. If you press ‘a’ in the caps locked state, you get an ‘A’ and the state doesn’t transition back to normal. If you press the shift/caps lock key again in the caps locked state, you get no character, but the state changes back to normal. Here’s a table:

 

State Input Output Next State
Normal A letter That letter, in lower case Normal
Normal Shift/Caps (the same key on Swiftkey) none Shifted
Shifted A letter That letter, in UPPER case Normal
Shifted Shift/Caps none Caps Locked
Caps Locked A letter That letter, in UPPER case Caps Locked
Caps Locked Shift/Caps none Normal

If you’d like, take a moment to walk through the table in your head. “If I’m in state Shifted, and I press a letter, I’ll see that letter appear in upper case, and then I’ll be back in state Normal.” The entire behavior of the keyboard (well, except for the emoji, special symbols, auto complete, and voice recognition) is modeled in this table.

Thinking of computer programs as state machines is super helpful. (Actually, I’m convinced a lot of tech support calls are because people don’t use computers in two separate steps: Get the machine in the state you want, then issue the command you want to do.)

What does this have to do with Zork? Well, the way that strings are decoded by the Zork story files is a state machine. Basically, there are a few states: lower, upper, symbol, abbreviation, and special. The way a program prints a string is by reading the next number, checking the state, and then acting on it. Like this:

  1. If the state is lower, the next number is an index into the lowercase alphabet.
  2. If the state is upper, the next number is an index into the uppercase alphabet, and the next state is lower.
  3. If the state is symbol, the next number is an index into the symbol table, and the next state is lower.
  4. If the state is abbreviation, pause this machine. The next numbers make an index into a table of where abbreviations are. Go look up that abbreviation, and decode the whole thing (using this same process) and insert it here. The next state is lower.
  5. If the state is special, the next two numbers form the bits of an ASCII character that we don’t have a symbol for somewhere else.
  6. If the next character is the designation of a state, print nothing, but switch to that state.

One nice feature of C# that I learned from reading C# in Depth is that you can use the foreach syntax to make your own state machine, and don’t have to write a lot of the boilerplate code for tracking what state you’re in. My somewhat messy implementation of this state machine is here.

Till next week, happy learning!

-Will