Recent Posts (page 36 / 65)

by Leon Rosenshein

Your Personal Board

Companies have a board of directors to provide oversight, guidance, and advice. They're generally not involved in the day to day operations of the company, and are composed of people with different experiences and backgrounds, often from other industries. They work to advance the company, but are not beholden to it.

That's nice, but how is it relevant to a developer? It's relevant because the main role of a company's board is to help the company progress and grow. As a developer you want the same thing. You want career progression and growth. And that's why you should have a personal board of directors.

Now a personal board is much less formal than a corporate board. There are no articles of incorporation, the positions are unpaid, and often the people on the board don't know they're on it. Your board is a set of people who you trust to give you honest advice and help open doors for you.

So who goes on your personal board? The specific people are up to you, but there are a few roles you want to fill. You'll want at least one traditional mentor. Someone you can go to and actively seek advice and guidance. The person you go to when you have a particularly thorny problem you need to work through. And you might have more than one if you have different types of problems.

You'll want someone who knows people and can open doors for you. Someone who can provide introductions to the people you really need to talk to. Whatever it is you're doing, it probably impacts and involves other people/teams, and this person can get you in touch with the appropriate counterpart on the other side and facilitate the initial discussion. Again, you might have more than one of these depending on what you're doing.

You'll probably need someone who can get things done for you. Someone who, if you get them on board, will bring others with them. A force multiplier so that after you get them on board you can focus on something else. This board member is the most situational, as that kind of influence is often specific.

Finally, and surprisingly, you're going to want an honest critic. Someone who can be critical of what you're saying/doing, and do it in a way that provides clear, actionable feedback. This is the person who helps you say things not just in a way that can be understood, but in a way that can't be misunderstood. They point out the holes and flaws in your arguments and help you fix them.

Remember, you're responsible for your career, so you should do what you can to improve it. That starts by getting the help and advice you need.

by Leon Rosenshein

Stories vs. Tasks

We Scrum. Or at least we Sprint. And our sprints come complete with Stories and Tasks. Just ask Jira. Stories and tasks are both important. Together, along with Epics and Initiatives, they make up our backlogs, both Sprint and Product.

Stories have phrases like "As <persona>",  "I should be able to <X>", and "so that I can <Y>". They have Acceptance Criteria that describe what done looks like. They often (almost always) require work on multiple components. And above all, when they're done the user/customer gets added value.

Tasks, on the other hand, are things that need to be done. They are defined by imperative statements. Like "Do <X>", "Automate <Y>", or "Write postmortem for <Z>". The definition of done for a task is implicit in its imperative nature. Just follow the instructions (along with all of its implied NFEs) and you're done.

What's important is not confusing the two. A Story will encompass multiple tasks, but the individual tasks are not stories. It's always a temptation, when a Story is too large, to break it down into smaller Stories. And we should. Being Agile, at its heart, is about continuously delivering value to the customer. If you can, deliver some value sooner.

What we need to avoid is breaking a Story down into tasks, and then treating the tasks like stories. Some Stories are just that big, or touch too many components, or are serialized in a way that keeps them from being done in parallel. In those cases avoid the temptation. To help planning and set expectations, create the tasks and add them to the backlog. Consider them and the time it takes to do them when doing Sprint planning. Tasks, in and of themselves, don't add customer value.

Remember. The goal is to add customer value, and to do that you need to know which things on the backlog add customer value and which things are steps. Because it's easy to maximize items closed/velocity without maximizing value add. And that leads to the dreaded feature factory, which we all want to avoid. But that's a subject for another day.

by Leon Rosenshein

A Fiddler On The Roof

Have you ever been on a high performing team? Not just a good team, with a good manager, where things mostly get done on time and people enjoy their work and team, but a truly high performing team? A team where you can't wait to start the day. Where problems seem to evaporate before your eyes and the team accomplishes more than anyone expected? The team has an outsized impact and other folks look at the team and want to join?

Most of the teams I've been on have been good, solid teams, doing good work and making a difference. I've also been on a few high performing teams, and that is a wondrous experience. You learn, you have fun, and you have an impact. The question is, what turns a good team into a high performing team?

It's not just having the smartest people on the team. How many all-star teams in whatever your favorite sport is could beat that year's champion? Very few, if any. Because while those players might be the best at their position, they haven't spent enough time as a team to play together. Back when I was building my first ethernet network none of us knew how to do it, only that it could be done. So we built a multi-cockpit man-in-the-loop simulator out of the tools we had.

It's not just having good management. Bad management can destroy a team. Micro-managing, information hoarding, and favoritism are all things that will keep a team from being high performing. On the other hand, the absence of those doesn't mean you'll be high performing.

So what makes a high performing team? I think there are a couple of enabling things. The first is a good mix of skills and levels. The team needs to have people who are willing to lead in all of the areas that need leadership, and willing to follow in the areas where there is leadership. Ideally you have people who have experience in the problem space and know some of the pitfalls, and people who are willing to learn and/or challenge the status quo and figure out new ways as well.

Second, you need to understand that performance takes time to develop, and there are stages people and teams go through on their journey to high performance. The best models I've found for these are Hershey and Blanchard's Situational Leadership for individuals and Tuckman's Stages of Group Development. And if you look at them you'll see that individuals and teams go through mostly the same stages.

But what turns a group of D3s (from Hershey and Blanchard), capable, cautious performers who are Norming (Tuckman) as a good team into a high performing team? That, as Tevye said,  I can answer in one word. Tradition. Ok, not tradition as Tevye described it, but a tradition of TRUST.

Trust within the team. Trust that the entire of the team is going in the same direction. Trust that you all have the same goals. Trust that the team has your back. Trust that the little things won't fall through the cracks. Trust that if someone says they'll do something it will get done, or you'll know it won't with plenty of time to adjust, Basically the folks on the team trust each other to work together in support of the each other and the team. 

Trust of the team. Trust outside the team that the team knows when it can make its own decisions, trust that it knows when it needs external inputs to make those decisions, and trust that it knows when to provide options to others for them to make decisions. In short, the organization trusts the team to do the right thing, not micro-managing or constraining the team.

Trust of the organization by the team. Trust that there will be support from the org. Trust that the overall goals and direction are not going to be changed on a whim. Trust that people are listening to the team. Trust that no one is trying to undermine the team. Or, in other words, trust that what the team is doing is important and that the team will be allowed to do it.

And maybe, when you think about it, that tradition of trust isn't all that different from the tradition that Tevye was talking about.

by Leon Rosenshein

Uncle Bob Says

Though we may try, and try, there are times when we cannot find a way to make the code express our intent; and so we resort, in the end, to a comment. 

We acknowledge that each comment we write is finally due to our failure to express ourselves in code.

    -- Uncle Bob Martin

I sort of disagree. Not with the first part. We should always try to write code that clearly expresses our intent. We use clear names. We break things down into digestible pieces. We try not to rely on side-effects. But sometimes it happens. Too many edge cases, the API that provides the information doesn't match the use case, or maybe it's just that complex. In that case we write comments to help the maintainer understand what is happening.

The second part though, I do disagree with. There are many comments that are not there to make up for failure to express in code, but to reduce cognitive load and to provide information that shouldn't be expressed in code.

Wait, Why do I need information in my code that shouldn't be expressed in the code? The most common thing is "Why". Your code can, and should, be as expressive as possible, and make it clear that you're setting the ignore_duplicates flag on your dB insert sometimes, but not others. What the next person wants to know is why you're setting (or not setting) it. That requires a comment. If the API you're using has some unusual quirks let the maintainer know. Sure, they might be able to find the source for the API and hope it's expressive enough to shed light, but don't subject them to that.

Another good use of a comment would be to explain why you're NOT doing something, If there's an API that appears to do what you want and you choose not to use it, say why. Otherwise the next person to come along will replace that carefully though block of code with a call to the API, and introduce the bug you were trying to avoid.

However, there's a responsibility there as well. You need to make sure that the comment is correct. And stays correct. If the code changes then the comment needs to be evaluated and potentially changed. There are few things in development that will make you want to hunt down the last person to change the code more than relying on a comment that's wrong. It feels even worse when you find out that the last person is you.

by Leon Rosenshein

Branch vs

Maybe I'm late to the party, but recently I learned about git worktrees. I'm familiar with branches, and they're great. Great for isolation. Great for bug fixes. Great for incremental changes. But they're not so great when you have large structural changes.

Or at least they're not great with structural changes in the presence of compiled languages and caches (both bazel and your IDE at least). From a correctness standpoint they work. You can switch between branches and your source code tree will faithfully represent the state of the branch every time. But unless your branches include the intermediate artifacts and state (which they shouldn't) then every time you switch branches your IDE will go nuts as it rescans everything and updates itself and `bazel` will invalidate itself and rebuild huge swaths of code. That's the correct behavior and it produces the correct artifacts.

But it comes at a cost. Depending on the scope of changes switching branches can cause as much work as a fresh `git clone` on a new machine. If you only do this once per branch that's not a big deal. But if you have to switch back and forth multiple times then swapping branches can become a major time sink.

My traditional approach to this has been to just `git clone` a new instance of the repo whenever I need to do this, and then remove it when I'm done. It works, but it burns 6+ GB of disk space for the `.git` folder every time, I need to remember to keep all of the master branches on my computer up to date (or at least in sync) across them, and syncing across them is hard if I need to since the common ancestor is up on github.

Worktrees solve this problem. The git repo isn't duplicated. The same one is shared for all trees, so I don't burn the disk space. There's only one master branch to keep up to date, so it can't be out of sync with itself. And the nearest common ancestor is right here on my machine, so sharing/cherry-picking is as straightforward as it can be.

And because it's a different directory tree on disk both bazel and my IDE don't lose their saved state when I switch trees. So I don't have to wait an extra 20 minutes for everything to download/build because some base version changed. I don't need to watch VSCode take over all my CPUs while it re-indexes the entire structure. I can name my tree (and the folder it's in) however I want to so that I know where I am.

So far it's working out well. It's much easier on my computer than just switching branches, and has less overhead for me than managing and syncing multiple `git clone`s. One thing I've been doing is having the new worktrees outside the base repo folder to make it easier on my IDE and make it easier for me to know what worktree I'm in. And it's simple to use. one 3 basic commands to remember. One to set up a tree, one to remove a tree, and one to list trees. After that I can switch trees just by changing folders, just like I would with a `git clone` based workflow. So far I haven't had any issues, but it's still early in my use of the pattern. Anyone already using `git worktree`? What's your experience been?


by Leon Rosenshein

Easter Eggs

Extra hidden code that does something fun. Seems like a good idea, and generally speaking I think they're a simple, mostly harmless bit of entertainment. I've enjoyed them. I've built them. Falcon 4's shared memory output started out as a development tool then became an easter egg, and eventually a feature.

They're an old tradition. The first one I ran into was the "Identify 9" easter egg in the old Wizardry series. Try to identify the 9th item in an 8 item list and you got 100M exp points. Then there's the Konami code, which works in all sorts of strange places. And you can still find them in recent tools. The `opus` tool had a couple, `starwars` and `wizard`.  You can find them in hardware too. From chip designers baking their initials into circuit boards to sneaking a Commodore64 program onto a rock album.

But more importantly, while they might be surprises to users, they shouldn't be surprises to the development team. Especially when you're working on mission critical or safety of flight sorts of things. And that's because they need to be tested. And not the standard developer testing of the happy path, but fully tested, including fuzz testing on any inputs and ensuring that they handle them gracefully.

For example.early versions of Microsoft Excel had a hidden flight simulator. It needed to be tested. Not to ensure that the flight model was high quality or that it obeyed physics, but that it wouldn't corrupt data. That it wouldn't suddenly crash and cause data loss. It's bad enough that people don't know the limits and loose data because they add too many rows to a spreadsheet. Image what would have happened if the data loss was caused by a bug in secret code. That wouldn't have gone over very well.

So don't be afraid to have some fun and to spark joy in your customers, but think it through, and don't do things in secret. That never ends well.

by Leon Rosenshein

Silos vs. Ownership

What's better, a very narrow, well defined scope or ownership of a problem? The answer to that, like so many comparative questions, is it depends. But first, what do those terms mean?

The term silo goes way back to when agriculture shifted from just in time/sustenance farming to commercial farming. It was a place to store all of a certain thing, and a place for everyone else to get it from. If you wanted grain, you went to the grain silo and bought/traded for some. You didn't need to care how it got there, and the people who filled the silo didn't care what you did with it. A very clean separation of responsibilities and both sides could focus on what they did best. The farmers handled filling the silo and the bakers turned the grain into bread.

In this context ownership is about the responsibility for something. It means you know who's responsible for getting something done. It shouldn't mean the person/team is doing everything.

Given that, what is better? Mostly it depends on how you define better. If you define better as "How can my team move the fastest?" then you want a really strong silo. You know what your inputs and outputs are, and you just build the best system for converting the former into the latter, You don't need to worry about anything or anyone else. It's up to those other folks to get you what you need and to take what you produce and do something with it.

On the other hand, if you define better as "How can I deliver my favorite feature to the customer the fastest?" you want to control everything. You want to control how the inputs are made (and everything that goes into them) you want to control how the feature is delivered. And you probably want to control the selection of features as well.

But there are problems with both. Silos, in the extreme, lead to building the perfect instance of whatever it is your building. That's good. As long as you're building the right thing. But if you aren't aware of what your customer is doing, you're probably building the wrong thing. And if all you do is demand a set of inputs, what happens when they're unavailable, or not available in the right ratio? You end up limiting what you can do because you're starved for inputs.

The other things silos lead to is a loss of responsibility. Everyone is responsible for their part and no-one is responsible for the whole thing. When you try to find out who is responsible for a change you want/need you don't get a straight answer. You get a lot of redirection and finger pointing that often turns into a circular dependency.

On the other hand, ownership has the opposite problem. It concentrates responsibility into one place, and that place becomes a bottleneck. Regardless of how much we tell ourselves we can multitask, there are only so many hours in a day, and there is a limit to how much can be done by that bottleneck. Context switching becomes hard, Things get lost in the context switch. Work gets serialized to make each part more efficient. At any reasonable product scale one person can't be responsible for everything and still be able to respond quickly.

So what's a developer or org to do? As I said at the beginning, it depends. You have resources and constraints and you want to maximize the output (customer value). Once you know what maximize means then it's just an engineering problem. And that's what we're really here for. To solve the engineering problems and maximize our customer value.

We do that by balancing silos (reducing blockers and cognitive load) with ownership (scope) so things keep moving AND continuing to re-evaluate the silos and ownership so that they work in service to the goals, not in opposition.

by Leon Rosenshein

Agile, 3X, And 4X

Almost 20 years ago a bunch of folks, including Kent Beck released the Agile Manifesto. Waterfall planning stopped being the right way to do things. It took a while, but it, and the internet, changed the way a lot of software is developed. Agile, Scrum, Extreme, and Mob programming are all based on the precepts in the manifesto.

15 years later, Kent had a thought

What if those waterfall folks aren’t wrong, what if they are solving a different problem than I’m solving? What problem is that?

This led to his 3X model, eXplore/eXpand/eBtract. 

  1. Explore: Try everything and figure out what you should be doing
  2. Expand: You know what you want to do. Things are growing almost faster than you can handle. You fix one bottleneck just in time to deal with the next one
  3. Extract: You've got growth under control. Time to extract value from the system. Economies of scale and efficiency dominate.

At different phases you value different things. When exploring, speed of experimentation and noticing results is key. Lots of things get tried and potentially abandoned if they don't work out. Short sprints and rapid iteration with plenty of feedback.

During expand you follow the hot spots. Quick fixes to buy time to give you a chance to do the work required for the next 6-12 months. Don't bother planning beyond that because things are still too fluid.

Extract, on the other hand. is when longer term planning is critical. Where do you need to be 3-5 years out to extract maximum value? Maybe not traditional waterfall with everything carved in stone at the beginning, but not breaking things along the way is crucial.

Uber as a whole is somewhere between 2 and 3, but different groups/teams are at different stages. Ride share (pre-Covid) has rapid, but stable/predictable growth, and we're extracting value. Eats is expanding. The general idea is understood, and enabling growth is key. ATG and Freight are mostly explore, with some expansion thrown in. But even within those large areas, different teams are at different stages.

That's Agile and 3X. What about 4X? Well, ever play any of the Civilization or Command & Conquer games or Settlers of Catan? One of my favorites back then was Stars!. Those are 4X games. eXplore/eXpand/eXploit(eXtract)/eXterminate. And the mental model is basically the same as Kent's 3X model, with a final phase of eliminating your opponents.

You already know how to think of things in the 3X model. The key is recognizing what phase you're in and adjusting your goals/methodology accordingly. So what stage is your team/group/org at, and are you using the correct approach for it?

by Leon Rosenshein

Context

Words have meanings. We have whole books that do nothing but list words and their meanings. Of course they use other words to define words, so it’s slightly circular, but somehow we’ve managed to make things work.

Now some of those words can have multiple meanings. Sometimes it’s the same spelling but different pronunciation, like lead, You can have lead the element as a noun, or lead the verb as in “The captain will lead us to victory”. Or it can be the same pronunciation, but different spelling, like their , there , and they're . It’s not too bad when writing, but in spoken English it can be a problem.

Which make communication hard, because it’s not just the words and their meanings, it’s the words around them. And the time. And the location. And the discussion itself. Because context matters.

In the ST;TNG episode Darmok that gets taken to the next level. The words are just labels for an entire story. And if you don’t know the story, how can you communicate?

So next time you need to communicate with someone, remember that its more than just the words, its the entire context. Otherwise you’ll end up in a conversation like this tiktok

by Leon Rosenshein

Now You Have Two Problems

Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems. -- Jamie Zawinski

Of course that's not strictly true. Even if you're not writing Perl you probably use a regex or two every day. After all, grep and ag (the silver searcher) take your input as a regex and use it to scan your file system.

And used correctly a regex is great for detecting and potentially extracting/transforming bits of a string. Need to know if a pattern appears in your strings? Regex is great for that. Need to know if a string is a phone number? There's a good regex for those things.

The thing to remember is that while a regex can scan text and has limited forward/backward looking ability, it's not a parser, and doesn't understand context. Arbitrary nesting of open/close pairs (I'm talking about you HTML) will drive the parser (and the developer) nuts, so just don't do it. If you're actually parsing HTML then use a DOM.

Even when a regex is the right answer, be careful. When your regex starts to become write-only code you've probably gone too far. Don't be afraid to split your regex into parts, capturing a bigger block of text and then using more specific regex's on it. It might not be quite a performant, but it will be more understandable and maintainable, and that's what we're really going for. When the next unsuspecting programmer (you?) looks at it in 6 months will they be able to understand what's happening?

And finally, a regex is a great place for an edge condition to hide. So when possible don't write your own. There are well known regex for parsing common things, email addresses, phone numbers, SSNs, credit card numbers, etc. If the thing you're looking for has a Backus–Naur form, there's probably a regex for it already, so use it. If not, the BNF form will help you generate the regex.