One of the seven principles of Lean Software Development is Defer Commitment. This principle tries to maintain options open (options have value) and avoid waste by only solving the current problem (avoiding speculative design). Following this principle allows us to generate easy to change, minimal systems with low accidental complexity and minimal features (as we have a low lock-in).
Keep in mind that software is only a means to an end, and that we are trying to generate impact, not software.
Over the years, I have found that postponing decisions and working in small, safe steps have been the key to creating sustainable solutions.
My experience tells me this is an excellent way to work, at least for product development.
This article is based on my experience over the past 12-13 years. All of the companies that I talk about are product companies, some of them startups (TheMotion, Nextail, ClarityAI) and others more classic companies (Alea Soluciones).
What we mean
Postponing all technical decisions until the last responsible moment. This practice implies scheduling irreversible decisions for the last responsible moment (LRM) and trying to make as many reversible decisions as possible.
What we do not postpone
For the last 12-13 years, I have been trying to work in an agile way, which implies that there are certain decisions in the way I work that I do not postpone, Agile, continuous delivery, eXtreme Programming, etc.
Some decisions are difficult to postpone. I think the language (ecosystem) and the approach to development (objects vs. functional) should not be postponed and must be clear from the beginning.
Some of these decisions may evolve over time, but we must have a solid starting point.
Working with technical excellence is another decision we do not postpone. It is implicit in my decision to work with Agile, but I think it is important to make it explicit.
Why we want to postpone / Benefits achieved
As we will see in the article, postponing decisions is not easy and requires practice. But as I mentioned in the introduction, it allows us to make sustainable and easy to evolve products.
- A later decision will give us a greater understanding of business and technology.
- Simpler and smaller solutions
- Less work :-) (less basal cost and less time implementing things we don’t need)
- More likely to not do anything that doesn't bring real value now
- More likely not to over-engineer
- Less effort in redoing work if necessary
- Our goal is to have a lot of real options... react well and fast to anything... and without fear...
- The more we postpone, the less we fill the backpack, and the less unnecessary stuff we add (less waste).
- We all know that it’s easier to move with fewer things in the backpack.
- Good architecture allows us to postpone important decisions, but this does not mean we are obliged to do so. However, being able to postpone them gives us a lot of flexibility.
This approach is aligned with the lean principles of eliminating waste and the agile principles of postponing until the last responsible moment (LRM) and is one of the approaches that can help the creation of good architecture. In turn, good architecture makes it easier to postpone other decisions.
How we do it
It is very difficult to explain everything that needs to be taken into account in order to postpone decisions/commitments without risk and achieve the benefits mentioned above. Anyhow, here is what has worked for us:
- We postpone decisions in a conscious and meditated manner.
- We consider a decision to be a good one when:
- It compromises us as little as possible
- Allows us to postpone other decisions
- It is easy to change/revert
- Attacks a current (not future) problem
- Good enough (no over-engineering, neither ours nor other people's)
- No Megaconstructions
- We reuse libraries but not Frameworks (we don't let them use us)
- We consider that everything can change (code/design/process)
- We work in a very iterative manner (See https://productdeveloper.net/iterative-incremental-development)
- When we start a new feature, we focus on identifying what we need to change in our architecture and our understanding of the ubiquitous domain/language.
- We assume that what we decided was the right thing to do at that time and in that context, and if it needs to be changed, that's fine.
- We put a lot of effort into good vertical slicing for the features (with the Hamburger method for example) and even technical slicing to be capable of working in small safe steps.
- We strive for technical excellence (Clean Code, Simple Design, TDD, Evolutionary Design, etc).
- We use DDD techniques and Hexagonal architecture.
The points I mention are not necessary, but they can serve as inspiration for any team that wants to gain a deeper understanding of agility.
What we need to make it easy to postpone
- Safety/Confidence: about our system and code and at the team level (psychological safety)
- Fast and continuous feedback: Customer feedback, System feedback (monitoring, observability), Development feedback (automatic tests, continuous code review with pairing, etc)
- Maintainable/Evolvable product/code base: We use XP, Devops practices and lean mindset
- Simple design
- Continuous refactor
- Accept/Assume that everything can be changed/removed/evolved (Features, Code, Design, etc)
We split medium/large changes into small changes, even duplicating code and duplicating data if necessary... all of this in order to be able to postpone decisions, not impede deployment and make incremental small, and safe changes.
In this section, I will describe some examples of systems in which we have had a lean approach trying to postpone all possible decisions until the last responsible moment. This has allowed us to have a very low time to market and to adapt the solutions to the feedback we were getting all the time.
Software for small telecommunications networks (up to 5000 subscribers). New fiber optic provision software with a very aggressive time to market:
- First rollout version with python serialization to file as persistence
- After one month, we added a python text search to the persistence layer
- After two months, we migrated the persistence layer to Redis to improve performance
- 5 or 6 months later, we migrated the persistence to MySQL with Full-Text Search once we saw the growth trends
At TheMotion, we decided to create a command line application to deploy, check status, create new services, and other common operations. The challenge was to make this application available as soon as possible and evolve it while changing TheMotion's system environment/infrastructure. Another important requirement was the security of access to the application.
- The first version was implemented as a shell script running in a server and executed by the developers via ssh (authorization, encryption at transit) using an alias.
- In less than a month, this shell script was migrated to python.
- And after several months, we removed the ssh call with a python client.
The developers' command executions remained largely unchanged during these months. In the background, the deployment, the infrastructure, and the running platform were rapidly evolving.
Also at TheMotion, we were creating a SaaS b2b product from scratch, and in the beginning, we didn’t had customers and the access was using an API. So we took a lean approach to user management, access, and role management.
- The first version only was usable by a few concrete companies, and we didn’t need to bill anything, so we only restricted access via IP address.
- The next step was to generate an ID and API Key for each customer and differentiate them by the API Key.
- After three months, we manually created users (name, email, etc), but without password, only the API Key.
- The next step was to allow some users to impersonate others (for example, an agency using the SaaS for several final customers).
- I don’t remember when, but later we created an admin website, and at this moment, we included passwords for the users.
You can see more examples and strategies in this blog post System: Control its evolution / or be its slave
I am also working on a workshop to practice how to defer commitment that will have more concrete examples.
Problems/Sensations generated by postponing decisions…
Certainly, postponing decisions is difficult since it generates feelings that we do not usually like:
- Conflict (as engineers)
Studies show that uncertainty generates fear and anxiety, so it's not surprising that we try to avoid it. Additionally, as software engineers, we have been trained to solve problems, which is why postponing decisions generates conflict.
Once you get used to working in small steps, with high quality and continuously iterating solutions, much of that anxiety and uncertainty disappears, and development becomes a continuous flow of change.
Postponing decisions and commitments is not easy, it’s difficult, and sometimes it can be confused with laziness or unprofessionalism. It couldn't be further from the truth. Professionalism is about solving current problems (YAGNI) with the most straightforward/simple possible solutions (Simple Design, pragmatism) rather than speculating about the future.
As we have seen, although difficult, postponing decisions/commitment until the last responsible moment has great benefits.
So, Postpone... to infinity and beyond…
Until the last responsible moment!!!!
References and related resources:
- Lean Principle #4 – Defer Commitment
- El arte del patadon pa'lante / Postponer decisiones técnicas (Spanish)
- How big is the backpack your organisation is holding
- Basal Cost of software
- It's not only waste; it's a burden
- Iterative and Incremental Development: we need both
- System: Control its evolution / or be its slave
- El arte del patadon pa'lante / Libro de recetas (Spanish)
Practices & Techniques
The post has been improved based on feedback from:
Thank you very much to all of you