#product #engineering - 7 mins read

Improve your development ROI 📈 with a lean approach

What is lean? 🤔

Generally speaking, when we talk about lean in product engineering, we're often looking at it from a team or organisation level.

We try to eliminate waste in the software delivery lifecycle by doing things like:

  • using agile methodologies
  • using DevOps principles to reduce hand-offs and bottlenecks etc
  • removing red-tape
  • improving communication and shared learning

For some organisations these changes require a fair amount of restructuring and with that comes risk and a cost.

While these are great principles to follow, they aren’t the focus of this article. This article will focus on optimising engineering at a low level with no risk and instant gains.

What is lean engineering? ⚒️

Lean engineering is all about removing waste and unnecessary processes from engineering.

It mainly involves being pragmatic rather than dogmatic, and following best practices when they add value, not just to tick a box.

Sometimes an engineering practice does add value, but it takes a lot of effort, and in fact, it might not be worth it or there might be a cheaper alternative.

Later in the article, we’ll see some techniques to optimise an engineer’s day-to-day workload, by:

  • assessing the return on investment of software engineering practices
  • removing any practices that aren’t worth the effort
  • finding cheaper alternatives that increase the ROI

Common mistakes 👎

One of the biggest mistakes I’ve seen is when a team decides to add a quantitative measure of a best practice and set some arbitrary threshold.

These quantitative measures aren’t an indication of value. All they do is distract an engineer with a box-ticking exercise.

“Every function should have a comment”

I’ve seen examples of this where engineers have written doc blocks that just repeat the function and parameter names to “tick the box”.

...
/**
 * Check whether a number is even
 * @Param number the number to check
 * @return returns true if number is even, false otherwise
 */
function isEven(number) {
	return number % 2 == 0
}
...

When the majority of functions have this sort of doc block, it means that all engineers just stop reading them, so by the time they get to a function that could use some explanation they’ve gone comment blind.

What’s the return on investment here? The engineers have wasted time adding pointless doc blocks, the reviewers have had to skim read through those pointless doc blocks, and then it’s made any future engineers blind to comments altogether. Hours could have been wasted each week here and there’s little or no value to be gained.

An alternative is to ensure the code is clean and easy to understand and doesn’t require explanation.

“All pull requests must have 100% unit test coverage”

I’ve seen this all too many times too. I’ve seen tests where the developer has made sure the code gets executed but no actual checks are done. This tricks the code analysis tools and shows up as 100% unit test coverage.

...
describe('Test the thing', () => {
    callAllTheCode();
    expect(true).toBe(true);
});
...

With unit testing, the return on investment varies by scenario.

In many cases, unit tests don’t cost very much to write and offer both short and long-term value. They can give the author immediate feedback that their code is working as expected without the need to deploy the code to an environment and run manual tests against it. Future engineers can then use those same tests to: help them understand the functionality ensure they don’t make a breaking change to the code.

Some unit tests offer very little value and often they’re the ones that take the most effort to write. For example, the code that handles an API request, let’s assume that the unit of code calls three other functions:

  • one to validate the request
  • one to transform the data
  • one to write a record to the database

Chances are:

  • you’ve unit tested your validation function with a whole bunch of different scenarios
  • you’ve unit tested your transformation function with a whole bunch of scenarios

So to unit test your API entry point, you’ve got to mock out three functions and then just check that they’ve all been called with the correct values. This test won’t give you many short-term gains as you’ve already unit tested a bunch of different scenarios with the validation and transformation logic, and the tests won’t offer much value to future developers because chances are any code refactors in this function will likely require the tests to be updated too.

In this example, you should probably cover this entry point with a couple of integration test scenarios which should offer more value than the unit test and would likely be less effort, and when combined with the unit test scenarios you’ll have pretty decent coverage.

Return on investment assessment 💰

Lean engineering assessment One technique you can use with your engineering team is to look at the various practices your engineers follow and attempt to measure the value they add versus the cost of doing them.

Many experienced engineers will make these assessments automatically as part of their process, but it can often lead to conflicts with more dogmatic or less experienced engineers who care more about ticking the box than adding the value.

In most environments, it’s best to “make it official” and do this assessment as a team so that everyone has a shared understanding and an opportunity to learn from each other.

Assemble your team

Get your engineers together, either physically or virtually around a whiteboard. You’ll likely need at least an hour for the session and to make things run smoothly it’s worth getting your engineers to start thinking about best practices ahead of the meeting.

It’s also worth setting an expectation based on what you’re working on. If it is just a proof of concept or a prototype you can probably throw out most of your best practices and just focus on rapid releases.

List your engineering practices

Allow some time for your engineers to collaborate on a list of engineering practices, these practices can be things that the team is or is not currently doing, and it doesn’t matter whether the engineers understand the value of the practice at this point.

You can do this by either nominating a scribe and calling out answers or getting your engineers to put sticky notes on the board.

At this point try to keep discussions to a minimum. No answer is right or wrong at this point.

Make a shortlist? 💭

The next step is to quickly eliminate practices from the list, if at this stage no one can see the value then just eliminate it.

Quickly work through the list and just check that at least one person understands the value of the practice. If someone sees the value, leave it there and move on, if no one sees the value just eliminate it.

At this point, you might end up breaking down or caveating a practice because there’s value in doing it in some scenarios but not others. Think about our unit testing example above, it might be that no one on the team sees value in unit testing a controller, but everyone sees value in unit testing X, Y, and Z.

Assess the return on investment ⚖️

The next step is to work through the list, let the team have a quick discussion about the value the practice adds, and make sure everyone has a shared understanding of the value. Consider what the alternatives are (and whether those alternatives are already on the list).

Look out for comments like “we do X in case Y happens in the future”. If Y never happens then does doing X add any value? Perhaps X should be done at the same time as Y.

If the team decides there is some value then we need to estimate the return on investment. There’s no hard and fast rule here and you might need to mix and match techniques depending on the practice.

Higher or lower

The simplest way to get a feel for the ROI is to get your team to vote on whether they think the added value is worth the effort.

If the results are fairly unanimous then job done, if not then you can either discuss it further or try delving a bit deeper into the quantitative measures.

Quantitative measures

Try to estimate the cost of following the practice and the cost of not following the practice. They don’t necessarily need to be the same unit. If they are the same unit, then it’s a nice and simple calculation. If not, it’s up to the team to use the estimates to make a call.

Writing integration tests takes 10% of the team’s time per week (developing, reviewing, waiting for builds, etc). Over a two-week sprint for five people (5 people x 8 hours x 10 days) that’s 40 hours per sprint.

On average the integration tests catch one issue per sprint.

At this point, the team can work out whether it’s worth 40 hours per sprint to catch that one issue, or would they be comfortable having that one issue make its way into production and having an extra 40 hours each sprint.

Once you’ve done this exercise the quantitative data can be quite useful to help drive the technical roadmap, and potentially decide to use an alternative approach. For example, you might decide it is worth investing 40 hours a sprint into dark releases or improvements to monitoring rather than integration testing.

In summary

Eliminating box-ticking exercises, maximising time spent adding real value and trusting your engineers to make the right call will not only optimise your development output but will also improve outcomes and make your engineers happy bunnies.