Recent Posts (page 29 / 67)

by Leon Rosenshein

Lessons From Buckaroo Banzai

But first, today is a day like any other day, but it's also different. Especially here in Colorado, but not just here. Shocking events are just that, shocking. And it takes time to process them. So take the time. Take care of yourself. Recognize that different people process shock differently. Don't expect today to be just like any other day.



I first heard the phrase Don’t tug on that in The adventures of Buckaroo Banzai Across the Eighth Dimension. Seemed reasonable at the time. Apparently it’s common advice in the fashion world, book restoration, and mushroom hunting as well. And it makes sense. Trim something to a clean end, make a small repair and move on. If you pull on that thread then you could easily distort something else in a different location and have an even bigger problem.

The same logic can apply to software as well. When you run into a problem where the situation you’re in almost matches the situation some library was built for, so you make a small change at the top of the library to handle your case. Then you realize that you need to go a little deeper, making another small change. And it happens again, and again. Until eventually you get things working for your case. But you find that all those small, inconsequential changes mean that the library doesn’t work in the standard case anymore.

Or maybe you find that you need to start using the latest version of the library. But that changes not only the interface, but a “bug fix” breaks a feature you’ve been relying on. So now you need to change all those use cases as well.

If the software world that has come to be called Yak Shaving. All those extra things that might be needed, but sure feel like procrastination, or maybe muda. And they certainly don’t feel like they’re related to the problem at hand.

And that’s where it gets interesting. Figuring out if it’s easier/better to fix/update that library (pull the thread) or change the code that has the problem to work with the existing library (cut the thread). And it’s made even harder because you don’t really know what the thread is attached to until you tug on it. So it’s hard to estimate which will be better in the long run.

When I approach something like that I often have a good estimate of the “cut” case, because it’s localized and I’ve been thinking about that area. If I can’t think of any other uses of the change (YAGNI) then why bother. If there are some possible uses then I might make an attempt at the library change. If I get to ~75% of the estimate and don’t feel any closer to resolution I’ll re-evaluate. At that point I know more about what it will take and the expected benefits. And being aware of the Sunk Cost Fallacy I can try to make an unbiased decision.

And on the other extreme, if changing the library is going to be a really good thing for lots of people and use cases, then I make the change. But not as a single PR. As a task unto itself that adds value. Then I make the original change.

by Leon Rosenshein

Tell Me About A Time When ...

I’ve been doing interviews for a while now. And one of the questions I like to ask candidates, of all levels, is to tell me about a time when they made a mistake that had significant impact. I find that there’s a lot of signal in the answer. There’s information on how self-aware the candidate is, what they find important, and how they respond to mistakes, both in the moment, and afterwards.

Because one of the follow on questions, if it doesn’t come up in the discussion, is “What did you, or anyone else, do to try and make sure it didn’t happen again?” And the response to that question has even more signal. It says a lot about how the candidate can generalize from specifics and how they think about the future. Answers can range from “I learned to do/not do X before I Y” to defining team/org processes as a way of reducing the likelihood of the issue to tooling with automated validation that prevents bad things from happening.

It’s those tools and automated validations that have the most impact on the future, and that’s what I really want to see in a candidate. Even with the most junior candidates or college interns I can end up having an interesting discussion of how a whole class of problems can be prevented. We can talk about Post-Incident reviews and root cause analysis and what you do when you find the root cause.

Because when it comes down to it, especially if you want to be able to get a good night’s sleep, how you prevent issues is more important than how you deal with them.

by Leon Rosenshein

String Style

Text is hard. Ascii is fairly straightforward, but Unicode is hard, and UTF-8 is NOT Ascii. Styling your text is hard too. Markdown is supposed to be simple and hopefully transportable, but …

And that doesn’t even get into how you type out an identifier, even if you’re using ASCII. Most (all?) languages use a space as a separator, so if you want a variable for the number of words in a paragraph you can’t use “number of words”. That makes sense. So eliminating spaces is a common pattern.

But there’s no one way to eliminate spaces. You could just drop them, or you could replace them. Or keep them? But what do you replace them with? An underscore is a common replacement, and sometimes, but not always, you can use a dash. And what about letter case?

For your pleasure, a list of possible string cases

Name/Example

Description

flatcase

Remove the spaces and don’t capitalize anything

UPPERFLATCASE

Remove the spaces. Capitalize everything

camelCase

Remove the spaces. Capitalize the first letter of all but the first word

PascalCase

Remove the spaces. Capitalize the first letter of all the words

snake_case

Replace the spaces with underscores. Don’t capitalize anything

camel_Snake_Case

Replace the spaces with underscores. Capitalize the first letter of all but the first word

Pascal_Snake_Case

Replace the spaces with underscores. Capitalize the first letter of all the words

SCREAMING_SNAKE_CASE.

Replace the spaces with underscores. Capitalize everything

kebab-case

Replace the spaces with dashes. Don’t capitalize anything

camel-Kebab-Case

Replace the spaces with dashes. Capitalize the first letter of all but the word

Pascal-Kebab-Case

Replace the spaces with dashes. Capitalize the first letter of all the words

SCREAMING-KEBAB-CASE.

Replace the spaces with dashes. Capitalize everything

Given all of those options, which do you choose? First and foremost, use whatever is in the file you’re editing. After that, use one of our style guides (C++, Go, Python). Then listen to your language and it’s style. And whatever you do, don’t use StUdLYcaPSfOrAnythinG

by Leon Rosenshein

Creep

Creep is not just a really bad acronym for political fundraising group. It’s also moving slowly forward, or in development terms, Scope/Feature Creep is when the requirements expand beyond the initial definition. The question is not if it’s going to happen, but what you’re going to do about it and if it’s a good thing or not.

Invariably, when you’re trying to do something you, and possibly no-one, has ever done before you’ll find that the work needed does not exactly match the work you expected. Some things you expected to need won’t be needed, and some things you never thought of (those unknown unknowns) will show up. You’ll often hear that called scope or feature creep.

But more likely, that’s not what’s happening. What’s really happening is that as you work on the project, you discover things that you didn’t know when you started. Or, as @jeffpatton said,

“Scope doesn’t creep, understanding grows“

And that’s a whole different kettle of fish.

First of all, if your understanding grows, that’s a good thing. The better you understand a problem the better you can make the correct tradeoffs needed to add customer value soonest. Sure, when you started you thought you needed to deliver feature A to the customer to add value. But that was before you really understood the problem. Along the way you learned that before you can do A, you need to do B, but guess what, B is valuable in its own right. So go ahead and deliver B as soon as you can.

Looking at it as understanding growing also has the benefit of making it more likely someone will say something. If you’re not punished for recognizing the additional work, or even better, rewarded for learning a new truth, think how much more likely people will be to tell you. If there’s more work to be done the schedule is going to change, whether you find out sooner or later. It’s better to find out sooner, so you can do something about it. If nothing else, you can be transparent. Best case, as I noted above, you find a way to deliver value earlier and everyone is happy.

So next time your understanding grows, don’t look at it as scope creep, look at it as an opportunity to add value sooner.

by Leon Rosenshein

Artificial Stupidity

Back when I was working on Combat Flight Simulator we had a problem. The game had varying levels of difficulty, but what did that mean? What’s the difference between Easy, Medium, and Hard? There were lots of things we did before we gave anyone a chance to try it out. Some were simple, like unlimited ammo and fuel, or invulnerability. Others were a little more involved, like changing how accurate your aim needed to be and the damage radius of things. Once we had that working we let others try it.

And it still wasn’t “fun”. This led to long discussions about what “fun” is, but one of the things we settled on was not just the ability to return to base successfully, but to complete the mission, and for air superiority missions, that meant stopping enemy aircraft. Unlimited ammo/fuel helped because you didn’t need to worry about having the perfect shot. Invulnerability or increased “hit points” meant you survived. But neither of those added up to stopping the other side.

What we found was that our AI was too good. Or at least never bad. We gave them a set of rules and models and they followed them. Exactly. Every time. Which meant that even if you knew the rules as well, if you made the smallest mistake they’d end up on your “six”. And that just wasn’t fun.

So what we ended up doing was implementing artificial stupidity. Depending on the difficulty setting, some number of the enemy AI and your wingmen will make suboptimal decisions. They might roll too far out of plane, or not turn at the right radius. And sometimes they’ll just do something random (simulating panic).

And of course, what’s old is new again. Seems like some folks working on chess AI took that approach and went a step farther. Not only does this new version make mistakes, it can make mistakes like a specific person. Of course, being able to predict a person’s moves, their tell, isn’t new. Apparently what’s old is new again. 

In retrospect, having your opponent be perfect isn’t much fun. We should have known that.

by Leon Rosenshein

Single Responsibility Principle

The Single Responsibility Principle (SRP) is a follow-on/extension to Don’t Repeat Yourself (DRY). It basically says that and given module should be responsible for one part of a program’s functionality.

Or does it? That’s the common understanding, but if you go back to the author and the original text that’s not quite it. In Robert Martin’s clarifying blog post you find something a little bit different. Instead of being based on functionality, it’s based on reasons for change.

Gather together the things that change for the same reasons. Separate those things that change for different reasons. -- Uncle Bob Martin

Which, while related to function, isn’t really about function. Consider HTML. There are multiple ways to style an element. If you want purple text you could put the declaration in every element. That would work, but it wouldn’t be DRY. You could define it in a div, then put everything in the div, but that adds an unneeded element. Or, you could put your style(s) into a CSS file. Not only do you get DRY, but you also separate responsibility. You’ve split the “look” of the site from the functionality of the site. The CSS changes when you want the color or padding or some other style element to change, but, in general, the functionality, both Javascript and HTML, don’t. 

Conversely, if you need to change what a button does, or change the new validation to a form then you don’t need to change the style. In fact, two people could make the changes in parallel and not have a merge conflict. That’s always a nice thing.

It also means that when the business needs change you all of the things related to that change are together. Changing the way the system responds to say, a pedestrian, could be handled individually by all of the things that notice pedestrians, or, you could gather all of the pedestrian related decisions together, making it easier to find and understand the interactions.

Of course, as with all architectural decisions, the specific answer depends on the specific case, it’s definitely another way to think about how to break things up.

by Leon Rosenshein

Tick-Tock

Commits and Pull Requests (diffs) are very similar, but they’re not the same thing. The basic difference is that git commits are local (ish) and PRs/Diffs are public. And there’s a lot of value in understanding the difference between your private and public record of changes. But what’s that got to do with tick-tock?

One way they’re related is refactoring. There are lots of reasons to refactor code, but they mostly come down to not knowing what you didn’t know. Yes, sometimes you just get the design wrong with full knowledge, but much more often either you didn't know what you were going to need at first, or the requirements changed after the fact. Regardless of the reason, there will come a time when you need to refactor to make a business (functional) change.

Another way to know if you should have more than one PR is to think about your PR message. The subject should be short, imperative, and have one goal. If your subject looks like conjunction junction you should probably split the PR.

And that’s where tick-tock comes in. You could do them both at once. A single moment in time that changes everything. But is that really the best way?

Maybe, but probably not. A better way would be to refactor first (tick), then make the business change (tock) as two completely separate PRs. There are lots of good reasons. Here’s some of the big ones:

  1. Your automated tests ensure that nothing changed with the refactor. You shouldn’t need to touch most of your tests. They should be testing the functionality, not the structure of your code. Of course if the refactor includes an API change then you’ll need to do some modifications, but the changes should be simple.
  2. You spread knowledge of the refactor by itself. If code is truth and documentation is context, the refactor speaks for itself and others are aware of it and can take advantage of it
  3. It’s easier for the reviewer. Each PR has one goal, so there’s less cognitive load when trying to understand it. The refactor should have no functional changes, and the functional change should only make the change
by Leon Rosenshein

A Question Of Balance

And it’s not just an underrated Moody Blues album

It’s been said that it’s important to write code for the maintainer, code that’s easy for a person to understand. It’s also been said that code should be decoupled and easy to change as requirements change. The problem is that these two goals are often in conflict.

One of the easiest ways to make code easy to understand is to make it very explicit. Make every decision and assignment in line. Maybe add some functions for readability, and so you can collapse the code in the IDE to it’s well chosen name. Don’t rely on anything else. And don’t use abstractions like interfaces and factories. Those just make it harder to find the code that’s running.

One of the easiest ways to make code easy to change is to abstract all the messy implementation details behind an interface. Then, when you need to support some new variant, you just update the factory and everything else is the same. Or use dependency injection so that your code doesn’t even know about the factory. The thing you need is just magically delivered on startup.

As you can see, there might be some tension between those two things. So how do you know what to do? As usual, it depends. It depends on where you are in the product cycle. It depends on what you know you’re going to need versus what you think you’re going to need. Because YAGNI.

by Leon Rosenshein

Experimental Results

We’re always doing experiments. Right now we’re probably doing more formal experiments than normal, but whenever you’re doing something new it could be considered an experiment. Depending on your knowledge and experience, it might be one with a much higher expectation of success than a traditional experiment, but writing code is just an experiment to validate the hypothesis that is the design.

Coding, like all experiments, will have a result. Since you’re writing the code to add value, you have a vested interest in a specific outcome. Sometimes though, the outcome isn’t what you hoped for. So how do you move forward at that point? One thing is for sure, don’t keep digging.

But even before that, what do you call that result. Remember, naming things is one of the hard problems, and that doesn’t just apply to methods and variables. It’s not a mistake or failure. Assuming you were thoughtful in your choices, they might have been incorrect, but they weren’t a mistake. While the code might not work as intended, the experiment isn’t a failure.

One way to think about is is with this picture:

Comparing outcomes to behaviors

And in this case, experiments, regardless of the outcome, have the most learning. If you take the time to learn from it.

So what do you call it when an experiment has an unexpected outcome?

by Leon Rosenshein

You're Sunk

Ever been in one of those situations where you know you’ve almost got something figured out so you keep trying? You dig and dig, making small steps until you finally reach a solution. That can feel really rewarding. But sometimes you look around afterwards and realize that while you might have ended up in the right place, the route you took to get there was suboptimal.

There are many potential reasons for that, and one of the more common is the sunk cost fallacy. The idea that you’re close to a solution and the time/money/effort spent on the current approach makes you feel like the best answer is to keep going on the same path. It’s certainly easier to just keep going. You don’t need to change direction. You don’t need to admit to yourself (or others) that you made an incorrect choice. And anyway, plugging along has worked in the past, so you expect it to work again.

The first thing you need to do is recognize the situation. And that can be hard (see above). One good way I’ve found is the WTF rate. If it starts going up, you might be in a hole. And like that digger, the first thing to do is stop digging.

The next thing to do is reevaluate. What were the assumptions going in? What have you learned since then? Are you really closer to a solution? What are you not doing because you’re so focused? Who should you be asking for help/advice?

It might be that staying the course is the right answer. You might be working on an onion problem. Getting configuration in a complex system correct the first time is like that. You don’t know what you don’t know, and the only way to find it is ask someone who’s done it or get to the problem and fix it yourself.

Or, more often, it’s an XY problem, and the best thing you can do is get out your rubber duck