Use external resources in a lazy way.

In computing nothing is free, always there is a cost and in a world with an increase cloud-based services and AI use, an overuse of external resources like files, databases, Web APIs and network can give a sad surprise when you see your Cloud Invoice. This principle is useful for on-promise operations too; in these cases, the economic impact can be camouflaged in a fixed cost but can require an unnecessary vertical scaling when a resource or several resources are running out.

Imagine you have a feat that needs to access several external resources, make a couple of database calls, make a call to a Web API, execute an algorithm that requires moderate CPU effort, and satisfy a set of preconditions and postconditions—nothing out of the ordinary. In cases like this, you might be influenced by the way the User Story was written and end up implementing everything in an order that—while it works correctly at runtime—isn’t efficient and consumes more resources than necessary in a high percentage of cases.

You could start by writing an initial version of the code based on the User Story (and preferably write the unit and/or integration tests first), and then analyze and refactor the code, looking for some of these scenarios:

– Does the order in which preconditions are evaluated allow you to complete executions before making unnecessary use of resources? Sometimes, simply validating preconditions as early as possible can prevent you from making calls to databases or Web APIs that would ultimately serve no purpose—since the execution won’t complete if the precondition isn’t met—or, even worse, might force you to compensate for a state change you made that you’ll have to revert.

– Is there any data that can be stored in a Level 1 cache (or a distributed cache)? If there is certain data that does not change very frequently, it could be stored in a cache, thereby avoiding a query to a database, a CDN, or a Web API. In this case, it is extremely important to clearly define the required consistency level and to have an appropriate invalidation mechanism in place.

– Is there a way to evaluate postconditions without having to allocate new resources?

– Are you sending and requesting only the essential data over the network? Sometimes you can use projections to avoid fetching a lot of data from the database that you won’t need later—the network, CPU, and RAM aren’t free. When you call a Web API, can you send and receive only what’s essential (GraphQL :))?

– If you’re using the Saga Pattern, are you performing the steps that don’t change state (and that would help you avoid compensations) at the right time, in conjunction with evaluating the preconditions?

These are just a few ideas that I hope will help you understand the principle. Of course, each situation will present new scenarios, but by consistently applying this principle, you’ll develop the habit of thinking and programming in a lazy way.

Leave a Reply