Recent Posts (page 33 / 71)

by Leon Rosenshein

Context

Context can mean a lot of different things in different contexts. Golang even has a built in interface for it. Today I’m using context in the context of communication. Specifically, context as the collection of thoughts, ideas, and biases two or more people bring to a conversation. Even more specifically, the context I’m talking about is the difference between the contexts the people in that aforementioned conversation have.

There are the things you know (your context), the things the other people know (their context), the things everyone knows (global context) and the overlap between those three (shared context). This is important because good communication starts from that shared context and builds out from there. Without that common framework you might connect, but probably not. Certainly not as well as both sides think they’ve connected. And guess what, The overlap between those three is much smaller than you think it is.

Non-overlapping circles

So what can you do about it? The best thing is to provide that shared context. And that means knowing who you’re talking to. Knowing the lens they see the situation through. Knowing what’s important to them, and the language they use to describe it lets you show that what you’re saying is important to them.

And don’t be subtle. Don’t show the facts and let them guess what your point is. Tell the whole story. What the situation was. What changed. What the situation is. Why the change is significant. And most importantly, what, if anything, you want them to do with the new information. Should they start doing something, stop doing something, do more/less of something, or are you just providing more context for future decisions?

Because context is the most important part. 

by Leon Rosenshein

Marie Kondo It

You know what kind of code has zero bugs? The kind you didn’t write. You might have less features, but I assure you there are no unhandled exceptions or edge cases in code that doesn’t exist. Less code equals fewer interfaces, fewer special cases, fewer leaky abstractions, and fewer hidden bugs.

But you can’t just remove things that don’t spark joy. Some things are simply necessary, not joyful. But you do want to remove things that diminish joy. Tha 3000 line file full of utility functions. All the places where you have custom code for the same purpose that almost match each other. Things that are almost done.

The KonMarie approach has 6 rules, and while written for tidying physical spaces, you can apply the same ideas to software as well.

Commit yourself to tidying up. Whether you call it tech debt, cruft, or code smell, start by recognizing that things could be better and allocate time to making it happen. It could be an ongoing percentage of your sprint budget, a dedicated sprint (or more), or maybe a specific story in your backlog

Imagine your ideal lifestyle. As with any journey, you need to know where you’re going or you’ll never get there. It could be a grand vision for an entire platform, or just removing the dread of going into a small area of the codebase

Finish discarding first. Start by removing the things that aren’t used. All the commented out code. The left-over helper functions. Unused levels of abstraction. The extra headers/imports. Then land the change. You’re not finished discarding until the change is part of the codebase.

Tidy by category, not by location. Once you’ve gotten down to what you need, gather like things together. Call it Single Responsibility, containerizing, lumping, or domain driven design, it’s about abstractions, mental models, and reduced cognitive load.

Follow the right order. Start with the easy things. Dedup pure duplication. Extract those couple of methods in a library that should be in their own library into one. Make the next step easier. Land each fix along the way. Provide incremental value and cleanliness along the way. Reward yourself and the team with the improvements.

Ask yourself if it sparks joy. As you go a-tidying, go in the direction that seems cleaner, not just to yourself, but to your customers and users. Because we all want to share the joy.

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.