The software we build is too complicated to build alone.
Most of my leadership effort goes into bringing the full weight of our
collective ability — all of us — to bear on the problem at hand.
Communication
We need input from engineers, executives, stakeholders, and customers to do our
work well. Because of that, I pay attention to:
- Frequent delivery to get feedback from stakeholders and customers
- I know the frustration of building for months and discovering it's the wrong
thing, and we could have known it a few weeks in if we'd structured the work
differently. Now, I work hard to avoid that waste by structuring the tech
and the goals so we can validate a small piece at a time.
- Strategic domain driven design so the engineers speak the same language as the
stakeholders and customers
- I know the futility of working for early feedback but having the essential
advice lost in translation — which we discover months later. "I told you X"
— "I thought that meant Y" — and so we waste our time. Now, I do my best
to deal with the human-to-human translation issues at their root by taking
time early on to agree on what we call things, and to express that in the
code and in our communication. We need all our minds' engaged on the work,
and constant translation is taxing like bartering in a foreign currency,
doing the conversion in your head after every counter. We can avoid this by
taking a little time to talk semantics and then bake the right terms and
ideas into our code.
- Technical practices to reduce cognitive load (so we can whole-brain one thing
instead of half-braining two things).
- code structure, testing strategy, tool choice, automation, just enough
documentation, etc.
- Social practices to make space for healthy debate
- We can't do creative work well when we are in fight-or-flight mode. I have
seen engineering cultures where truth matters more than delivery (which
seems like it would be efficient but turns out to shut down discussion),
and others where delivery trumps all (even if we say nothing, to our
detriment).
- I earn trust, facilitate working agreements, coach
radical candor,
model humility and
saying sorry, and I have hard conversations when they're needed
Technology
- Keep it as boring as possible
- Make intentional choices
- Move fast on reversible decisions (and slower on those that are harder to
change)
- Design just enough to know where to start; build just enough to know what's
missing from the design; repeat
- It's not done until we know it works (which probably means it's covered by
automated tests and deployed to production with appropriate metrics to see
that customers are using it and that it's going ok)
- We can't know it's tested unless we practice TDD (or run mutation testing
tooling regularly — but I haven't seen anyone run those since my testing
course in university)
Product
I prefer to work on products where we prioritize quality over the number of
features — I want to do good work, which means I need to work in an environment
where customers want good work. I respect that there are markets where customers
care more about low cost than high quality, and I choose to avoid working in
those environments.
On the other hand, whatever we build, we build to sell or serve to others --
let's pay close attention to "good enough" and avoid building extra that nobody
needs (but somebody would have to pay for). The best way I've found to do this
is to build in small steps to unlock the next step in the user's workflow, as
described in User Story Mapping (Jeff Patton)
Mentoring
The best way I've found to get excellent senior engineers is to train junior
engineers. The best way I've found to do that is to give them as much
responsibility as they can handle (maybe a little more), and to coach them
through the challenge. I do this with their permission, in proportion to their
level of interest and aligned to the direction they want to take their career.
I have an imaginary "coach vs deliver" dial in my mind (imagine a spedometor),
and the more urgent our business need, or the larger our undelivered inventory,
the more I shift from "coach" to "deliver". With a delivery focus, I am
comfortable stepping in more often with concrete direction. With a coaching
focus, I make space for less experienced developers to do more exploration on
their own. In that coaching mode, I'm more guiding than directing. When business
constraints force me into delivery mode, I work hard to deliver and get quickly
back to coaching mode.
If we are feeling the delivery pressure, then we could use a few more
independent and skilled developers — so the coaching is especially important. I
do as much of it as I can, as often as I can, while respecting the current
urgent needs.
2023: My Technical Approach
This mind map shows the skills and techniques that I used most often as
of 2023.
2023: Senior Software Engineer
Here's how I approached work as a senior software engineer.
Bio
— Strengths, values, and motivators
How I interact with others
-
customer focussed:
I've found the only way to build software that actually gets used is to work closely with our customers from the very beginning and throughout the work. This might mean customer interviews, watching them work, User Story Mapping, releasing early prototypes for feedback, collecting thoughtfully crafted metrics, and repeating those things throughout the work.
-
collaborative:
I'd like to achive more than I can as a solo developer. I need the perspectives, inspiration, and experience of many peers to do my best so I work hard to sustain and enhance an environment where we can all bring our best to the table. In most teams I am part of I share practices like collaborative programming (ensemble/pair/mob) and retrospective techniques which help us spend more of our effort creating together and wasting less effort negotiating and coordinating with each other.
-
communicative:
I apply practices like continuous integration, User Story Mapping, Domain Driven Design, Acceptance Test Driven Development, and Architectural Decision Records to facilitate cross-discipline communication early and often.
How I build
-
test driven:
Acceptance Test Driven Development at a system capability level; Test Driven Development at the class level. I can find ways to test drive very hard-to-test systems, either with creative testing approaches or strategic refactoring.
-
adapting to change:
I engineer the system so it's cheap to change so we don't have to predict the future to be successful. That includes modular architecture, appropriate testing, and refactoring skills (practicing them personally and sharing them with team mates).
-
measuring over guessing:
I use spikes, experiments, production metrics, and other mechanisms to compare reality to our plans so we know that we're building the right thing. Guesses are timeboxed and we gather data to course correct.
-
lean flow:
I apply practices that reduce work in progress, queued work, unreleased inventory, and other lean waste we can have more impact with the same effort. I share practices like collaborative programming, continuous integration, small commits to help me and my peers spend more effort building/delivering and less effort coordinating/correcting.
What I build
-
polyglot:
Comfortable with Java, TypeScript/JavaScript, Bash. Familiar with Ruby, C#, C++, Python, VBA.
-
database agnostic:
Comfortable modelling and testing relational data with SQL and NoSQL based databases.
-
multi-architecture:
Comfortable building distributed systems in the cloud, domain driven microservices, and modular monoliths. I prefer hexagonal architecture because of how easy it makes unit testing.
-
refactoring:
Very comfortable with advanced refactoring methods (IDE tooling, manual provable refactorings,
test && commit || revert
).
-
learning:
Always keen to learn a new tech stack, pattern, or approach.
-
oncall operator:
Experienced as a first responder to production incidents, including emergency performance optimization, security incident response, and other high severity customer-impacting incidents.