Minimalist Software Product Development

Remove is improve!

Nick Gibbon
Pareture
Published in
8 min readMay 23, 2024

--

In this post we consider how to apply aspects of minimalism and essentialism to software delivery. Where broadly you focus on less stuff with more meaning both physically and mentally. This relates to concepts like ‘quality over quantity’, ‘start less, finish more’ and stewardship (caring for things) more generally.

‘Have nothing in your houses that you do not know to be beautiful or believe to be useful.’ — William Morris.

In contrast in modern software product development I often see many people focusing on how a shiny new vase will rest on the mantle piece just as there’s rubbish everywhere, the boiler’s broken and the back window is smashed. Yes, you can technically live like this but it would be better if you didn’t.

I am advocating for an intentional, continual process where we step back, zoom out and consider the socio-technical system holistically. Through this introspection and retrospection we try to simplify things and in doing so we get close enough so that we see clear opportunities for other valuable improvements. And then as we make make changes to evolve the system we remain cognisant of this perspective which helps us to make better decisions.

We can do this with a layered approach. To go back to the house analogy; we clean and tidy all of the rooms. We reorganise things so they make the most sense. We find and fix clear problems and we do small improvement jobs we identify. Then we focus the rest of our efforts on only a few of the most valuable bigger improvements and try to finish them. And we do all of this continually. We only consider moving house once we’ve maxed out what we’ve already got.

I’m not naïve enough to suggest everything should be perfect at all times but we can remain in touch enough that we aren’t starting fires when we’re covered in petrol. We can’t do everything at once but we can make sure that each thing we do is focused and valuable. We can’t avoid taking on any debt ever but we can do so consciously and manage it properly over time.

Benefits

  • We properly understand where we currently are. All further evolution is better contextualised and validated. A great pitfall of many change initiatives is not truly understanding where you are to start with.
  • We are able to get the most out of what we’ve currently got by ensuring higher efficiency and less waste.
  • The mindset lends to needing less costly rework in the future.
  • You can scale. Scaling products in terms of scope and users only works if you continually simplify. Otherwise as complexity and debt increases change rate slows, failure rate increases and more people are needed.
  • With a little discipline we can spend most of our time on high value, planned work. Without which we spend more time firefighting unplanned work and dealing with the negative consequences of this effort for people including various degrees of burnout and lower motivation working on things that feel lower quality.

Just as you can have death by a thousand cuts. You can have success by a thousand small positive actions. We do not need silver bullets.

Backlog

The backlog is for work that you will do and that you might do at some point in time. It isn’t for anything you won’t do or that is already done. Continually ensure that your product backlog is a reflection of your actual reality. Some issues can be a bit ambiguous so do the work to make them more clear. You can also aid organisation by using metadata to group and filter work in whatever ways might be useful to your context.

Documentation

I have often heard people complain about documentation wikis as sort of useless dumping grounds. But what’s the alternative? No centralisation of information? Just depend on e-mail chains and whatever someone can remember / make up in a given moment? Silly. These same people never want to put any consistent active effort in to structure, organisation, curation, edits, removals, archiving etc. They don’t value this type of work but for some reason they can’t see how the outcomes relate to the inputs (or lack-thereof). It’s like never gardening and then wondering why your garden is overgrown. Classic project vs. product mindset stuff.

Don’t be one of these people. Invest in to ensuring your documentation has a logical structure where everything has a home. This helps with discovery and evolution as people can basically work out how to find stuff and where to put stuff. Don’t depend on search alone. Experiment with different structures. Look at how other projects organise documentation in the industry. Consider user groups / personas / stakeholders. Don’t confuse long, rough, rambling discovery work with short, sharp feature overviews. Consider how to handle docs which are useful to many parties. Consider different types of hierarchy. Think about titles. Use links and inheritance to avoid repetition. Importantly, continually keep living content up-to-date and remove / archive content that is no longer relevant.

Processes

Continually ensure that your key processes; onboarding, contribution, quality, release etc. are up to date and visible. Continually introspect to consider simplification of the steps involved.

GitHub Gardening

Other SCM platforms are available.

Handle issues as described for the backlog. Prune, merge or protect branches. Merge, give feedback or close pull requests. Ensure repository ownership, policies and descriptions are up to date. Archive or delete repositories which are not used. You might wish to consolidate misc content in to a single repository for your team / product.

Again, the point here is to ensure that your assets consistently reflect their true state. It’s very common for teams to have 15 repositories, 1,000 branches and 100s of open pull requests when they only really use 8 repositories and have ~20 changes in progress across them. The order of magnitude differences due to lack of active management does make things more confusing.

Operations

To achieve things we often need to combine many different tools, products and services. As we apply the resultant mash-up to various problems it is often the case that all of the functionality is not needed. The baseline solution needs to be broad so it can handle many use cases but each application only needs a subset of the capability. Knowing this we should endeavor to fit things accordingly and the reduced surface area outcome will be more reliable, more secure, more performant and cheaper. When you run less stuff you consume less resources, there is less stuff that can go wrong and when stuff does go wrong there is less stuff that can confuse the resolution. Stuff. This is analogous to deleting apps on your phone that you don’t use or not emptying the whole workshop when you only need a screwdriver.

My recent experience is with container orchestration. In this case I can use a container-optimised OS which is a distribution with a smaller footprint. Less utilities downloaded and less processes running. Within a cluster I can choose to turn features off that I don’t need and not deploy agents for different integrations if I’m not using the integrations. This all makes it easier to focus on the things that do need to be there.

I’ve also found that homogeneity in terms of things like products and versions of tools within an organisation can really help with efficiency of operations and technical problem management. Things should only be done with variation for good explicit reasons. Try to avoid situations where this type of skew / drift occurs arbitrarily.

Source code

When considering source code I mean all technical bits and bobs that are part of a product or part of the value stream for that product. That being all of the steps that turn an idea in to something that is running for a user. All of the automation and quality activities. All the related high-level code, configuration, DSL, files, queries etc. What is right may always be context / domain / subject dependent. But there are general simplifying rules that can help.

  1. Analyse and remove everything that isn’t being used. Files. Scripts. Test data. Dependencies. Variables. Lines which are unreachable. Functions, classes, modules. Dependencies. If it’s unintentional or due to organic growth then clear it. If it’s intentional because you think something might be useful at some point or you don’t want to lose something clever then find another way to keep it somewhere else because it causes confusion. Then for everything else that is being used you want to organise it as best as possible.
  2. Keep all comments and documentation files up to date. Code can get very messy but it never truly lies because it runs. Documentation is not like this.
  3. Be DRY. Don’t repeat yourself. Reuse where you can. Ensure you scope things appropriately to avoid accidental dependency. Don’t reinvent the wheel. Use external dependencies to achieve things. But consider how much of any import you are using. Can you import only a part of the library? Does this really require an external import at all?
  4. Be idiomatic. Embrace the conventions of the language / tools you are using and don’t try to force things to be other without strong reason. Use linters, follow good industry practise and work to coalesse with your team on implementation standards removing unnecessary variation consistently. Always comment / explain a deviation when needed for future posterity.
  5. Refactor. Here is where simplification overlaps with reliability and performance. As time goes on you may understand your problem better and have access to new knowledge or perspectives. As you get more familiar with your inputs and outputs the middle bit can become more clear too and require changes. You can often remove a lot of confusion by making things more readable and in doing so the implementation is naturally more efficient than before.

As alluded to already, continually doing this type of work and being close to your systems helps you understand exactly where you are now and lets you organically identify gaps and opportunities.

Features

At a higher level of abstraction, whether technical or user-facing, ensure that new features in development (especially bigger projects) are valuable such that they are give-or-take the best use of a teams time. Watch out for creep. Do all you can to ensure you are building things that solve problems that people want solving. Measure twice cut once. Ensure you create feedback loops. Advocate for the end user. Focus on UX. Simplifying the experience is as important as pure function.

Finally, DEPRECATE (and then remove) FEATURES. Get the right stats and visibility. But if something is not being used or is not part of the products strategy then you need to find a way to put it to rest. This can be a painful process in terms of activities and communication but it make a great impact. Reduced surface area removes the maintenance burden, increases security and streamlines the mental model for the user; bringing benefits to all parties and unblocking the future. Features which need to be deprecated but are still hanging around are baggage that weighs us down!

--

--

Nick Gibbon
Pareture

Software reliability engineer & manager in cloud infrastructure, platforms & tools.