Book Notes: 'The Effective Engineer' by Edmond Lau

Posted on 2022-03-07

The Effective Engineer by Edmond Lau is a book about how to be more effective within your organization. Note that there is a difference between “effective” and “efficient”. Maximizing “effectiveness” requires you to make sure that you’re working on high leverage activities. It doesn’t matter how efficient you are at completing tasks if the tasks don’t product business value.

I thought the book was good, and there wasn’t really anything that I took issue with. Some parts of the book are probably obvious to people with work experience. I would definitely recommend the book, especially to engineers that are frustrated by the impact they’re delivering to their teams.

Here are my notes:

What does it mean to be effective?

  • Effective people deliver business value
  • Effective people are efficient in doing so
  • Doing one, but not both, of the above means that a person is less effective than they could be
  • Lau defines an effective engineer by “the rate at which he or she produces value per unit of time worked”

Which activities should you focus on?

  • Focus on high leverage activities
    • Defines leverage as “value produced per time invested”
    • AKA: ROI on effort
  • Relates leverage to the Pareto Principle, where 20% of activities produce 80% of value
  • High leverage activities might have a fixed cost with long-tail payoffs
    • Creating great onboarding documentation
    • Learning system shortcuts and utilities
  • Andy Grove’s methods for increasing leverage
    • Reduce the time required to complete an activity
    • Increase the output of an activity
    • Shift to higher leverage activities
  • Software engineering example of Grove’s points
    • Automate manual parts of development or testing
    • Triage features based on the value they provide
    • Talk with customers to see if a different feature can provide more value with less engineering effort
  • Focus on leverage points, not easy wins
    • Easy wins can be high leverage (my own caveat)

Optimize for learning

  • Prioritize environments and projects where you learn a lot
  • Adopt a growth mindset
    • People with fixed mindsets may avoid growth opportunities because they think they’re not capable of delivering
  • Learning has a compounding effect
    • Learning follows an exponential growth curve (my note: more heuristic than fact)
    • Small improvements to learning have outsized impacts
    • Learning earlier contributes to larger total “value” in the long-run

I want to point out that I find the (common) example of learning as compound interest to be…shaky. I’m not convinced that the vertical axis (“knowledge”, “value”, etc.) is a compelling metric. It isn’t clear to me that the model accurately describes the phenomenon.

I have “known” many things throughout my life that I have completely forgotten about - my answers for “how does the spanning tree protocol work?” would be very different today than 10 years ago.

That said, I think it’s a decent heuristic and motivator for people to learn more. I suppose that’s what counts.

  • Seek work environments conducive to learning, characterized by:
    • Fast growth
    • Training
    • Openness
    • Pace
    • People
    • Autonomy
  • Dedicate time on the job to develop new skills
    • Study code written by the best engineers at your company
    • Write more code
    • Go through internal technical material
    • Master the programming language you use
    • Send code reviews to the harshest critics (good tip!)
    • Enroll in classes
    • Participate in design reviews
    • Work on a diversity of projects
    • Make sure you’re on a team with senior engineers
    • Jump into code you don’t know
  • Always be learning
    • Books
    • Side projects

Prioritization

There are no shortage of things to focus on, so how do you choose? Focus on high leverage activities by:

  1. Identifying which tasks directly produce value
  2. Focus on tasks that are important, but not urgent
  • Track every task in a single list that is:
    • Easily accessible
    • A canonical representation of work
  • Repeatedly revisit your tasks and decide whether there’s a higher leverage activity you could work on

Work on tasks that directly produce value

“Activity is not necessarily production” - Yishan Wong

“Value” can be measured in a variety of ways: user growth, sales, business metrics, etc.

Always consider opportunity cost. Even though a task might produce value, doing it means not doing some other task, which may produce more.

Focus on the important and non-urgent

Things that are important but non-urgent:

  • Planning and prevention
  • Building relationships
  • New opportunities
  • Personal development

If you only ever focus on fire-fighting crises, you may miss out on long-term opportunities that aren’t pressing. You can also take note of which crises frequently come up, and see if they’re symptoms of a larger problem that needs to be solved.

Protect your maker’s schedule. Limit the amount of things you’re working on concurrently. Make a habit of prioritization.

Iteration Speed

Continuous deployment is an example of how you can improve iteration speed by reducing overhead.

“Move fast and break things”

My note: MFABT is integral to delivering value:

Invest in time-saving tools

I personally think this is very important, and I think “tool” can take on a range of meanings. Writing scripts to do tedious tasks is an obvious example, as long as it’s worth it. I think learning the vim way of editing text has been a massive productivity boost. I say “vim way” because I mostly use vim plugins inside of IDEs. Making an “IDE” of Frankenstein’d vim plugins has never worked out well for me. Being able to nimbly edit text, however, pays off every time I need to write anything on a computer. I even use vim shortcuts to navigate web pages.

Lau’s tips:

  • Get proficient with your editor/IDE
  • Learn a productive, high-level language
    • “Dense” languages can iterate faster than verbose ones
    • REPLs provide immediate feedback and debugging
    • Less boilerplate
  • Get familiar with shell commands
  • Prefer the keyboard over the mouse
  • Automate manual workflows
  • Make it easy to run unit tests

Measure What You Want to Improve

Choosing what to measure helps you focus on the right things. It’s helpful to identify metrics that are directly related to the work you’re doing, to ensure that your efforts have the impact you expected.

Metrics can also:

  • Help guard against future regressions
  • Drive forward progress by making sure they’re trending in the right direction
  • Measure your effectiveness over time and see if there’s a higher leverage activity you could have been doing

Pick the right metric - some can actually harm your output if you optimize for them:

  • Hours worked vs. productivity
  • Average response times vs. 95th percentile response times
  • Bugs fixed vs. bugs outstanding

The metrics you choose will influence the behavior you take.

Be skeptical about data integrity. Using iffy metrics can win debates, but hurt the organization in the longrun.

Validate Your Ideas Early and Often

It’s important to validate ideas early to make sure you’re working on the right things. No one likes working on something for 6 months only to find out that it doesn’t even solve the problem - because no one actually talked to the people with the problem. Get feedback as early as possible.

MVPs are a common way to do this. MVPs are covered extensively in other books and blogs. Continuously validate changes with A/B testing.

Beware the one-person team

Working for too long without feedback can cause a number of problems:

  • Working on the wrong thing
  • Solving the problem sub-optimally
  • Obvious design flaws

Avoid these risks by:

  • Being open and receptive to feedback
  • Commit code early and often
  • Request code reviews from tough critics
  • Bounce ideas off teammates
  • Design the interface of a new system first
  • Send out a design document before developing code
  • Structure ongoing products so there is shared context with teammates
  • Solicit buy-in for controversial features

Improving Project Estimation Skills

  • Decompose the project into granular tasks
  • Estimate based on how long tasks will actually take
  • Think of estimates as probability distributions (great tip)
  • Let the person doing the work estimate the tasks (also a great tip)
  • Beware of anchoring bias
  • Use multiple approaches to estimating
  • Beware “the mythical man-month”
    • There isn’t a linear relationship between “people on the project” and “time the project takes to complete”
  • Validate estimates against historical data
  • Use timeboxing to constrain scope
  • Allow others to challenge scope

Balancing Quality with Pragmatism

Establish a sustainable code review process. Benefits of code reviews:

  • Catching bugs or design faults early
  • Increasing accountability for code changes
  • Positive modeling of how to write good code
  • Sharing working knowledge of the codebase
  • Increasing long-term agility

These benefits can come at a tradeoff of iteration speed.

Manage complexity by choosing the appropriate abstraction (sounds easy when put that way). Choosing a bad abstraction can seriously hamper development velocity.

Automated tests and strategically paying off technical debt can have an outsized impact in the longrun.

Minimize Operational Burden

The first part of this chapter is basically saying - choose boring tech.

Automate mechanical tasks, like:

  • Validating code behavior
  • Extracting/transforming/summarizing data
  • Detecting spikes in error rate
  • Building and deploying software
  • Capturing and restoring db snapshots
  • Periodically running batch computations
  • Restarting a web service
  • Checking code to ensure it conforms to style guidelines
  • Training ML models
  • Managing user accounts or user data
  • Adding or removing a server to or from a group of services

This list is pretty broad, but it’s helpful to give people ideas.

Make batch processes idempotent

By making them idempotent, you can deal with failures (network, application, etc.) more gracefully.

If you can’t make it idempotent, try to make it reentrant (I would like to see an example of this).

Idempotent batch processing also lets you surface failures appropriately. Lau gives an example of running a check every 60 seconds, instead of 5+ minutes, and only raising the issue if the error repeats on subsequent tries.

Plan and practice failure modes

Netflix Chaos Monkey example

Invest in Your Team’s Growth

  • Get everyone involved in hiring
    • Coordinate priorities on what’s important - algorithms, aptitude, etc.
    • Periodically evaluate your approach
    • Design interview problems with multiple layers of difficulty
    • Keep interviewing a high signal-to-noise ratio by short-circuiting rambling
    • Check for red flags
    • Pair with teammates
    • Try unconventional interview approaches
  • Work to improve onboarding process
    • Ramp up engineers quickly
    • Impart the team’s culture and values
    • Expose new engineers to the breadth of fundamentals needed to succeed
    • Socially integrate new engineers onto the team
    • Onboarding talks
    • Starter tasks (great idea)