Onboarding when you don’t have access to the team

Mitchell Hashimoto, Contributing to Complex Projects:

The first step to understanding the internals of any project is to become a user of the project. You do not have to become an expert user, but my personal graduation criteria for this step is to try to build something real using the project, even if it is small or simple.

Analog: here’s a functional area. Set it up for yourself or on your localhost. Now, make a small, well-contained change.

Learn how to build the project and get a working binary (or equivalent). Don’t bother with understanding the build system, the dependencies, etc. Just cargo cult guides, websites, whatever you need to reliably and repeatedly go from source code to runnable binary on your system.

Analog: get to the point you can run the app, run (focused) tests, and see changes in the app. Then start trying to make functional changes.

To learn the internals, I like to use an approach I call “trace down, learn up.”

Analog: for your first several changes, read from the top of the call stack down as far as you can. Don’t try to make changes, but do try to note all the landmarks (files) you visit and how they relate to each other. Note “side quests” to investigate later as you go.

Don’t be afraid of complexity. I think too many engineers look at stereotypically complex projects such as programming languages, browsers, databases, etc. as magic or as destined for higher-beings. I like to remember that all projects were started by other humans. If they could do it, I can do it too. And so can you.

You, too, can gain enough understanding of an eight-year-old system to work on it as though you were around when some of it was written. In fact, your effort will compound: the longer you’re around, the more you will find curious code that, it turns out, you added in the first place. 😆

Perspective, you want it

Perspective is the lens we view our world, work, relationships, etc. All the luck, resources, or knowledge in the world are wasted without good perspective. If we’re talking about life like it’s a role playing game character sheet, you want to have a good perspective stat/multiplier.

Some clever tricks:

  • keep the mind open and flexible to other perspectives; seek them out
  • practice at holding many perspectives simultaneously
  • know the limitations and strengths of a perspective as you navigate the world
  • know when your default perspective makes a scenario more difficult and how to fall back to a perspective you still believe in
  • get out of routines periodically and see if it changes how you see things
  • more so, get out of your bubble; see people of a different background live their lives, reflect on what factors brought them there and how factors are different/similar for your life
  • even more so, travel outside your city/state/country; axiomatically the people most different from you live in a place far away

It’s often tough to gain perspective. Most of the defaults in life steer us away from insight. School, cliques, work, even typical travel nudge us toward seeing familiar things with similar people who live similar lives. I’m by no means an expert at breaking out of these ruts. I’m pretty enamored with my routines. Unfortunately, I don’t have a clever trick to offer here.

Julian Shapiro, What you should be working on:

What is admirable is periodically killing your momentum to ask, Should I still be doing this?

Michael Lopp, The Art of Leadership: Small Things, Done Well:

Let others change your mind. There are more of them than you. The size of your team’s network is collectively larger than yours, so it stands to reason they have more information. Listen to that information and let others change your perspective and your decisions. Augment your obvious and non-obvious weaknesses by building a diverse team. It’s choosing the path of least resistance to build a team full of humans who agree with you. Ideas don’t get better with agreement. Ideas gather their strength with healthy discord, and that means finding and hiring humans who represent the widest possible spread of perspective and experience. Delegate more than is comfortable. The complete delegation of work to someone else on the team is a vote of confidence in their ability, which is one essential way that trust forms within a team. Letting go of doing the work is tricky, but the manager’s job isn’t doing quality work, it’s building a healthy team that does quality work at scale.

Be like Bill Gates and Warren Buffett: If you’re not spending 5 hours per week learning, you’re being irresponsible

Former president Obama perfectly explains why he was so committed to reading during his presidency in a recent New York Times interview (paywall): “At a time when events move so quickly and so much information is transmitted,” he said, reading gave him the ability to occasionally “slow down and get perspective” and “the ability to get in somebody else’s shoes.” These two things, he added, “have been invaluable to me. Whether they’ve made me a better president I can’t say. But what I can say is that they have allowed me to sort of maintain my balance during the course of eight years, because this is a place that comes at you hard and fast and doesn’t let up.”

Leadership keywords

My current theory of leading software teams and projects has four keywords:

  • Trust: I assume everyone is working to get the job done. They assume I will help them get the job done. This starts off more like faith and grows into trust as teams coalesce.
  • Autonomy: each person on the team is independently productive for a significant chunk of their day. When they make assumptions to stay unblocked, they are adept at collaborating asynchronously to verify them or correct course.
  • Agency: each person solves the task they’re working on in a way they see fit, within the conventions shared by the team. If an interesting idea comes up outside of the those norms, anyone can pursue it such that they maintain the trust/faith of their colleagues.
  • Support: each person knows that the team, particularly yours truly, is there to help each other. This most often manifests as pairing on troubleshooting, designing, coding, etc. Most importantly, sometimes it is sharing the load when one person is feeling overwhelmed.

Support is a recent addition. I had previously thought that autonomy and agency were the things enabled by trust. But I’m starting to think1 support is a crucial part of the equation too.

Without support, you’re just throwing people into the pool and telling them they can stay a-float however they like. It omits the “get good enough to swim” part, which is pretty crucial!

This kind of support is most obvious when you’re bringing someone new onto a team. But you need it throughout an individual’s tenure on your team. The people with years of deep experience and history in their head need support of a different variety.

Teaser: I’m on the fence about adding 2-3 more words to my repertoire. There’s a lot of moving parts to leadership!

  1. Largely due to onboarding people to a team/system/organization with a long history. This doesn’t happen without a larger-than-normal support effort. Perhaps that effort is amortizable over time (i.e. writing docs), but it’s still a big lift.

Like caveats? Try writing about leading teams!

It’s tricky to write about leading software teams. Herein, reflections, not complaints, on pursuing higher software leadership truths. Many of which are riffs on 4 Reasons Writing About Software is Hard:

Writing is actually an incredibly relevant skill for engineering leadership (and engineering in general), but it’s still hard. You can have all the insights in the world, and still struggle to convey your message or find the right audience.

First off, writing is hard so writing about software is hard too. Developing our thoughts beyond “that sounded nice in the shower” is hard but rewarding work.

It’s difficult to translate “this worked” out of the system of people, circumstances, and goals. As noted above, at one scale everything works, at another basically nothing works.

No advice on software development is universal. From the smallest coding details to how we structure our multi-person/week projects, there are no best practices. There’s only “this worked/failed for us when building this particular project”.

Writing about software leadership ends up being a lot of describing the people and scenarios that led to a successful approach. The trick is that getting down to particulars about people and scenarios is either too personal to share on the internet or too specific/proprietary to make sense outside a specific organization. It’s hard to write high-quality leadership ideas without drowning in setup, hedging, or over-generic characters.

It’s tempting but insufficient to suggest “act as a good person would and most things will work out”. Merely acting as a good person does will not get me out of situations where individual best intentions created bad outcomes. Perhaps specifying what a good person does with enough clarity that another person can apply and/or emulate it is a laudable step.

In practice, it seems better to say “act as a good person 95% of the time but do the minimally jerk-y thing 5% of the time to cut Gordian knots created by good intention”. I’m not happy with that trade-off, but it does seem like a necessary part of leading people. I build context so I can trade accumulated trust for impact when the situation requires it. (Hopefully! I often don’t know if I was spending from a surplus or deficit of trust until much later.)

Benchmarking Rails apps in 5 bullets

  1. When in doubt, measure. Twice!
  2. For ad-hoc/napkin estimates, I use Benchmark.ms { …the code… } to size up the performance of Ruby code.
  3. When I want to do The Science to compare approaches, I use benchmark-ips. It works a lot like Benchmark, but does all the cold start, iteration, and math for you. It’s great, thanks Evan!
  4. When it comes to code that interacts with databases (Postgres, ElasticSearch, HTTP APIs, Redis, etc.) it is almost always the case that one big query is far faster than queries inside a loop (e.g., N+1 queries)
  5. Ruby performance is often limited by creating many objects and the time it takes for the garbage collector to find/free them up afterwards. This is sometimes not the case in recent Ruby versions (see #1).

Bonus useful tools:

  • bumbler – profile loading gems from your Gemfile at application boot. In most Rails apps, there are several seconds of savings to find in lazy loading rarely loaded libraries.
  • active-record-query-trace: shows the call-site and last few stack frames for every query in your development log. Super handy for “where is this blasted query coming from?”

The Beatles 🤝 Timeless leadership lessons

The Economist, The Beatles and the art of teamwork:

Take the role of Ringo, for example. When he is not actually playing, the band’s drummer spends most of his time either asleep or looking bewildered. When the other three musicians bicker, Ringo smiles beatifically. To a casual observer, he might appear dispensable. But musically, nothing works without him, and as a team member he softens conflict and bridges divides.

More teams need Ringo(s), those who make it work without soaking up emotional energy.

Managers who think that building esprit de corps requires a separate activity from work—here-comes-the-fun time, set aside for axe-throwing or gif battles or something equally ghastly—are missing a fundamental point. The highest-performing teams derive the greatest satisfaction not from each other, but from the work they do together.

The Beatles weren’t excellent due to vacationing together in India. They were exceptional because of what they built together, both because of and sometimes despite each other. So it goes with teams.

Better know a standard library

Read your current/new language’s standard library. Highly recommended for developers of all experience levels. You’ll pick up the idioms, you’ll discover something useful. You’ll laugh, you’ll cry, you’ll wonder if they’re making a sequel!

My favorites are prelude.hs (which, sadly, does not seem to be the file that Haskell bootstraps itself from any more 🤔) and Rubinius’ implementation of Enumerable in Ruby.

(The reference is from The Colbert Report, which is a fun-but-dated thing to know.)

Offloading fast operations in Ruby by data structure

Noteflakes: A Compositional Approach to Optimizing the Performance of Ruby Apps — the idea is to offload “inner-loop”-type operations from Ruby to C-extensions. The clever twist is this happens via data-structure-as-language. Ruby being Ruby, you can wrap a DSL around the data structure generation to reduce the context switch from Ruby to offloaded operations.

There’s precedent to the approach: if you squint, it’s not unlike offloading the math for computer graphics or machine learning to a GPU. That said, the speed-up is unlikely to be as dramatic.

I hope to hear more of this approach in the future!

Adjacent: “it’s wild how much of the 2021 programming ecosystem is declarative data structures evaluated by recursive functions.”

Don’t be spooky

It’s possibly the best advice for managers I’ve given so far. When you’re communicating with your team, lead with context and reassurance. Never message someone on your team, “let’s talk when you get a minute”. That’s void of information and scary as heck!

I have to remind myself of this when I’m rushing. It’s faster to ping someone to arrange a synchronous talk than it is to write out what I need to say and cover all the bases. But that doesn’t give me license to skip all the context. Broad strokes are okay. An information vacuum is not okay.

Accidental spookiness invites story-crafting. Minds race. Lacking information or context, we tell stories. They often aren’t happy stories, regardless of how good your relationship with the team. We humans are better at convincing ourselves to fear something (survival instinct) than the other way around.

Avoiding spookiness reduces the chance of people telling themselves negative stories. Context and clarity counteract reading the tea leaves and world building. Even more important, it prevents people from pre-gaming the conversation. That way, they don’t prepare for a conversation that happened in their heads, instead of one that’s about to happen. (Avoiding pre-gaming is important on both sides of the conversation, as it turns out.)

A corollary to “don’t be spooky” — deliver constructive but critical feedback as close to the “original sin” as possible. Receiving feedback that you did poorly weeks after the fact is disconcerting. It can lead the recipient to wondering what other things they’re doing poorly but won’t hear about until later. Which leads to story-crafting, and the whole negative cycle starts a-new.

Give your team enough context to pre-game conversation based on the real context, not conjecture. And don’t hold on to feedback for “that perfect time”.