What’s A Cipher?

“Cipher” really just means “encryption technique”. A cipher is a set of steps for encrypting information.

The first example they gave us in my Cryptography class was the “Caesar Cipher”, which got its name from the fact that Julius Caesar used it to encrypt his communications.

This text has been encrypted with the Caesar Cipher:

PHHW PH DW FKLSRWOH DW QRRQ

In cryptography terms, the encrypted message is called “ciphertext”.

Here’s the text before it was encrypted (a.k.a. “plaintext”):

MEET ME AT CHIPOTLE AT NOON

Do you see what the transformation was?

The encryption is performed by shifting each letter in the plaintext three places down the alphabet. I take the M and convert it: M -> N -> O -> P. I take the E and convert it: E -> F -> G -> H. And so on.

If I hit the end of the alphabet, I wrap back around to the beginning. So if I were to transform a Y, the conversion would look like this: Y -> Z -> A -> B.

I could pick any number of spaces to shift my plaintext characters into ciphertext characters. Here I picked 3. That’s my “key”. The rules for a Caesar Cipher are the same regardless of the key I pick, but the output is different depending on the key. If someone knows the key, they can easily decrypt the ciphertext, shifting each letter by 3 in the other direction.

The state of information security in 50 BC was not so hot. At least by current standards. The Caesar Cipher is about as simple a way of encrypting information as one could imagine. It’s easy to encrypt your information with it, but super easy to crack, even if you don’t know the key. At most, you have to try 26 keys before you’re successful.

Modern encryption is much more sophisticated, of course. But we have to start somewhere.

It’s Not The Plan, It’s The Planning

“The best laid plans of mice and men often go awry.” – Robert Burns

“No plan survives contact with the enemy.” – Helmuth von Moltke the Elder

The world is chaotic. It’s complex beyond our ability to make predictions. You can’t possibly anticipate what it’ll throw at you.

But if you’ve thought through the possibilities you can think of, you’ll be better prepared to react, even to the unexpected.

“In preparing for battle I have always found that plans are useless, but planning is indispensable.” – Dwight Eisenhower

“Plans are of little importance, but planning is essential.” – Winston Churchill

The act of planning puts your mind to work on the problem. You take stock of the situation. You consider possibilities, evaluate strategies, weigh potential outcomes, prioritize.

And when your precious plan meets the enemy and is quickly rendered useless, you still have all those strategies and priorities in mind. You can adapt with confidence.

What Do Your Heroes Have In Common?

What do all of your heroes have in common?

Maybe several things, but one thing almost certainly:

Work ethic.

There are probably plenty of folks out there who could’ve been your heroes, but they never put the work in. They never finished the thing and put it out there, so you never saw it.

Beginner’s Mind

“In the beginner’s mind there are many possibilities, in the expert’s mind, there are few.” – Shunryu Suzuki

Keeping a beginner’s mind does not mean that you must be a beginner. It means that regardless of your level of advancement, you approach your work or study with the open, humble attitude of a beginner.

“The mind of the beginner is empty, free of the habits of the expert, ready to accept, to doubt, and open to all the possibilities.” – Suzuki

Beginners notice details that are invisible to experts, because experts know things. When we know things, we think less about them. And thinking less about them means we’re less likely to notice when, for example, they become less important, or don’t apply to the problem we’re trying to solve, and there’s a better way.

Beginners are more likely to fail, so they’re more open to failure. Experts often have the role of “expert” to lose if they fail.

Beginners think more about questions than answers. Experts think more about answers than questions.

“If your mind is empty, it is always ready for anything; it is open to everything.” – Suzuki

We’re never complete. There’s always more to learn.

What Unit Tests Are For

Unit tests are not for finding bugs.

Unit tests test components (units!) individually, so they can’t find problems that come up when two (or more!) components need to play nicely together. (We use end-to-end tests for that.)

Unit tests are for designing small software components. They specify how your component works and let you verify that.

Good unit tests support refactoring: They allow you to make changes that don’t affect a component’s observable behavior, and then quickly verify that you haven’t broken the observed behavior.

Bad unit tests are another story for another time.

Refactoring Is A Design Practice

Refactoring is a skill. We get better at it with practice.

Refactoring is also a habit. Some of us should do it more. Some of us should do it less. (My opinion is that most of us should do it more.)

Software design is a skill. We get better at it with practice.

Let’s not be tempted to think that design is a thing we practice rarely, or a thing that only “architects” are concerned with. Refactoring is a design practice. So a little bit of design happens every time we refactor.

Cultivating a habit of refactoring is a way to build our design muscles. It’s a thing we can do every day. We can do it badly. We can try something crazy and throw it away if it doesn’t work. We can try, try again. That’s how we get better.

What Refactoring Is (And Isn’t)

We just can’t have nice, clean terms with humans around.

I bet most developers hear the term “refactoring” used in context from other developers and assume it’s just a fancy word for changing your design. Same here. But no. It’s a more specific thing. And its value lies in that specificity.

“Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure.” – Martin Fowler

You aren’t refactoring unless you preserve observable behavior. You might break the interface of a module for minute or two, but you should very quickly have a working system back, with no difference in observable behavior.

Renaming a method is refactoring.

Moving a method from one module to another one where it might make more sense: refactoring.

Taking several lines of code that perform a coherent action and extracting them into a method: refactoring.

Refactorings tend to be tiny moves. They’re made in the service of improving your design incrementally. They’re often made with the sense that you don’t know yet what will be a good design. But you think this small move is a step in the right direction.

Adding new behavior is never refactoring, but a little bit of strategic refactoring may be very helpful to do before you add a particular feature.

Use The Interview

“So, do you have any questions for us?”

They asked me this question at the end of many interviews in my young software development career. I’m ashamed to say that I answered it with “no” a few times.

They shouldn’t have to ask you this question. You should use the interview to learn about them as much as they use it to learn about you. Not just at the end, but from minute one.

The software industry is fraught with undesirable employment situations. How are you going to avoid them? You need to do your homework. The interview is a big part of that. It’s your opportunity to get information directly from a team you’re thinking about working with. You should get as much information out of it as you can. (You should also be aware of the source, of course, of course.)

I’ve been thinking about questions to ask potential clients/employers during an interview to get a good idea what the work will be like.

What are some areas of improvement for your team? What are you doing to address them?

This is important on a few levels. On the surface, you want to know what this team you might be joining is capable of and what they struggle with. Maybe their answer to this question will give you a chance to say “I can help you with that.” That’s a sign of a good fit for both parties.

A little deeper: Knowing the kinds of problems they’re thinking about will tell you a lot about what level the team is working at. If you’re interviewing for what they’re calling a junior dev gig and they tell you their team is struggling with problems you’d consider trivial, that’s a sign that either the gig is not for you or that you should start angling for a more senior role.

Deeper still: You want to work with people who are self aware and humble. People who know what they don’t know and aren’t afraid to admit when they don’t know.

What are some areas of improvement for you individually? What are you doing to address them?

See previous question. Also, for an individual, it’s helpful to know what role they play on a team for context when you hear their answer to this question.

Have you ever had to deal with a toxic team member? How did/would you handle that situation?

I’d watch their reactions closely when asking this question. They might give away the presence of a toxic team member on the team you’re about to sign up for. Or you might see signs of good camaraderie. Their answer should at least give you some idea of the interviewers’ communication skills and emotional intelligence.

What happens here when people make a mistake?

I wouldn’t expect to hear that people are treated poorly when they screw up, even if it’s true. I would watch them closely as they answer this question to get a sense of that.

What I want to hear here is that mistakes are addressed rationally and without animosity. “What went wrong? How can we make sure it doesn’t happen again?” (Bonus points for five whys or similar.)

How much up-front or long-term planning do you do?

Do they try to plan the whole project up front and then execute it? Do they do this with features? Do they work in iterations? How long are the iterations?

How often do requirements change while work is in progress?

When this happens, how is this addressed? What happens if development is almost done on a feature and the requirements change substantially?

How frequently does the team interact with the customers?

Do they try to minimize interaction with clients/stakeholders, or do they encourage it? How early/often do they deliver working software to customers for review?

How frequently does the team interact with users?

(If customers and users are different sets of people.) How often do users see work-in-progress? How does the team make sure users want what they’re building?

How closely does your team collaborate?

As a feature gets defined, designed, developed and tested, does that process involve siloed work with handoffs, or is everyone involved from beginning to end? Or something in between? Do the programmers pair?

What forms of communication do your team prefer?

Is the team co-located? If not, what kind of remote collaboration tools do they use? What means of communication are most used on the team? (Examples: Meetings, phone/Skype conversations, impromptu face-to-face chats, email, IM, chat, etc)

How does the business evaluate the progress and success of your team?

Do projects begin with fixed budgets? Fixed scopes? Fixed timelines? Which of those does the business tend to be flexible about when things get tight?

Do you practice continuous integration?

How about continuous deployment? Do they run regression tests regularly? Do they run a nightly build? Do they do a build with every commit? How often are commits made?

Do you value good software design?

What does that even mean to them? Do they have unit tests? How have unit tests affected their software designs? Do they write tests first? How frequently do they evaluate the design? What form does that evaluation take?

Do you ever ship prototype code?

Yuck. This happens when quality isn’t valued. That could mean the team doesn’t value it, or that the organization around them doesn’t, and the team caves to pressure to ship ship ship.

Higher-Order Functions

A higher-order function is a function that does stuff with other functions. That is, it either takes at least one function as an argument or it returns a function. (Or both.)

JavaScript’s Array.prototype.map is a familiar example:

function square(x) {
    return x * x;
}

let numbers = [1, 2, 3, 4, 5],
    squares = numbers.map(square);

console.log(squares); // 1,4,9,16,25

Here, numbers.map applies the square function to every element in the numbers array and returns a new array with the results. We call the function “map” because we think of its operation as mapping each value in the original array into the new array, transforming the value using a mapping function.

A higher-order function that returns a function is perhaps less intuitive. Here’s a simple but contrived JavaScript example:

function lessThan(x) {
    return y => y < x;
}

let lessThan100 = lessThan(100);

console.log(lessThan100(50)); // true
console.log(lessThan100(500)); // false

Here, lessThan is a function that returns another function. We immediately call it with 100 to create a function called lessThan100, and then that function, when called, returns a Boolean value that describes whether its argument is less than 100.

Many popular programming languages now have what we call “first-class functions”. That just means that you can do things like we’re talking about: passing functions as arguments, returning them from other functions, assigning them as variables, etc. The language treats functions as first-class citizens.

Passing functions as arguments has become pretty common, in my experience. I regularly see functions that do this, often for mapping or filtering data.

Functions that return functions seem far less common. I rarely see them in the wild. Yet.

Something About Horses and Water

Teaching people who don’t want to learn is extremely frustrating. They listen impatiently, if at all. They argue. They interrupt. They preen. They try to correct or one-up. When they don’t understand something, they pretend they do, or they insist it’s not an important thing to learn. They don’t learn and everyone involved has a bad time.

Teaching people who want to learn is extremely satisfying. They listen eagerly. They respect your expertise. They appreciate that you’re trying to help them. When they don’t understand something, they ask. Not only do they learn, but they apply their new knowledge. Everyone involved has a good time.

You can’t help people if they don’t want your help. Direct your energy toward folks who’ll appreciate it.

(And be someone who appreciates it.)