Friday, January 03, 2025

Decide as Late as Possible: Product Limits

 Translated from the original article in Spanish https://www.eferro.net/2024/07/decidir-lo-mas-tarde-posible-limites-de.html

As we mentioned in the previous article in the series on Lean Software Development, we will continue exploring techniques that allow us to make decisions as late as possible.

We begin by systematically defining Product Limits.

When developing an increment of the solution we are implementing, it is essential to establish concrete limits on all parameters that might introduce complexity. This allows us to focus on what provides value now and postpone more sophisticated solutions, avoiding additional costs and complexity. Over time, these limits will evolve and force us to modify the solution, but this approach allows us to delay each decision and avoid the cost of developing and evolving that more complex solution until absolutely necessary.

It is crucial that when defining a limit, it is incorporated into the code or solution so that, if exceeded, the application behaves in a controlled manner, alerting the team and possibly the user.

Examples of Limits I Have Used:

  • Total number of customers/users.
  • Number of concurrent customers/users.
  • Maximum file sizes that can be uploaded to the system.
  • Quotas per user (storage, requests, number of entities, etc.).
  • Numeric values for any business concept (in the problem domain).
  • Response times for various requests.
  • Resolutions/Devices for the UI.

If we do not clearly define these limits numerically, we open the door to speculative design to address situations we are not yet facing.

Examples of Using Limits to "Decide as Late as Possible"

Knowing and defining the total number of customers on various occasions has allowed me to offer very simple solutions for persistence that were useful for months before requiring changes. For example, if the number of users is small, using file-based persistence and loading the information into memory is feasible. It also allows us to use solutions like SQLite and postpone the decision to introduce a separate database engine.


By limiting the size of requests (in terms of volume) and defining connection scenarios, we can offer robust and simple solutions, processing requests synchronously. This postpones the need for an asynchronous solution. For example, on one occasion, we needed to allow users to upload files to the system; the initial implementation only allowed very small files. This enabled us to create a simple implementation and obtain very quick feedback (in less than 2 days). A few weeks later, once we saw that the functionality made sense, we improved the implementation to support larger files.

In several situations where each user accumulated storage (files/objects), defining a product limit for the total storage for all users, another limit for each user, and another limit to indicate when we needed to start worrying about this issue helped us postpone implementing any control and management measures for this storage until one of the defined limits was reached.

To illustrate the systematic use of these limits with a concrete example, at Alea Soluciones, we launched a new product for managing/controlling a fiber network and the routers of end customers in less than 3 months. We knew our clients at the time had no more than 1,000–2,000 users. We knew the operators of the management system were no more than 2–3 concurrent people. We also knew that to gain more users, our clients often had to deploy fiber to the home or at least visit the user’s home to install the router, meaning growth was limited to 2–5 users per week. With this context, the initial version of the management system was a web server that processed everything synchronously, storing user information in memory and persisting changes to a file as needed. This allowed us to allocate much more time to other system components (integration with fiber headers, remote router configuration systems, router monitoring systems, etc.). Of course, the system evolved, and we improved it, but we always waited until the last responsible moment for each decision to introduce new technology.

Another simple example comes from ClarityAI, where, to create a chat-ops tool in Slack offering some internal platform capabilities, certain limits were defined, both in maximum response times and in the volume of information processed. By defining a high maximum response time (2s) but lower than Slack's supported time for synchronous responses to commands (3s), we were able to postpone implementing an asynchronous solution for quite some time. This application handles information about technical inventory components (code repositories, Docker repositories, etc.) and teams and people. We saw it was easy to define maximums for each of the elements, and in all cases, the maximum was below 1,000 items. These limits allowed us to avoid significant complexity, simply relying on a NoCode backend (Airtable) as the database, which also provided a basic administration frontend. We know perfectly well that when we exceed these limits, we will have to consider a more sophisticated and scalable solution, but postponing that decision has allowed us to develop this application very quickly for over two and a half years.

Related Resources:

No comments: