Victor Mingueza

Coding Architect & Platform Engineer

Helping companies to streamline their operations through automation and service lifecycle design, based on industry best practices, so that they can focus on delivering value to customers, quickly. All these while improving the developer experience to reduce toil and frustration.

A Plea to Avoid Bipolarism in Software Design

Published on: 23-10-2023
TLDR;

I would like to make a call to avoid the bipolarism that is growing in software design, the chase for traffic and attention leads to this extremism, in a world where pragmatism should reign and where generalization becomes very difficult to prescribe without more context

Social networks and their impact

It does seem that more and more we live in a world where algorithms drive the internet traffic and where chasing public attention is becoming the new high. Unfortunately, it has been shown in documentaries, like The Social Dilemma, that these algorithms tend to favor extremism to drive engagement.

The software industry is also growing quickly, with the rate of software developers continuing to grow exponentially, even with that, demand is still outpacing capacity, as the world becomes more and more automated. This rapid expansion of software developers creates a need for more educational content that is usually found on social networks like YouTube. Sadly, good content quality doesn't seem the please the god of algorithms. Wouldn't it be ironic that these algorithms end up damaging the industry as a whole for the chase of profit in corporations?

The monetization of content on public platforms seems to accelerate this trend, where daily views are more important than the viewer's satisfaction with the platform and fidelity. Who even gets recommendations on YouTube from their subscriptions? what are they even for? This focus on short-term achievements driven by financial pressure to raise the value of stock seems to dominate the markets and consequently, the educational content which is concentrated in a few platforms.

In software design, pragmatism should reign, there is not only black and white, there is a whole scale of greys, but grey might be a color that doesn't bring enough attention.

Clean Code vs Performance

If you look into clean code, you are likely to see the tension around clean coders on one front led by the original author, Robert C Martin, and a growing trend of developers bashing clean code due to their performance impact.

bipolarism in software design

As stated above there is not one extreme or another, but pragmatism and experience should lead the conversation to create a fitting solution. Let's look at some of the points made by both sides and see how there is always more to it and why context should drive these decisions.

A case for clean coding

Clean coders favor abstractions to improve the readability of the code, like abstracting complex conditions into methods or exposing methods through interfaces to decouple implementations, this helps with the cognitive load and decreases the complexity so that code is easier and faster to reason about. But this has an impact on the performance of the code, how much? That is where it gets complex.

Calling an interface is more expensive than calling a simple function, or is it? The reality is that it depends on things like whether the execution will use dynamic or static dispatch, Rust exposes these decisions to the developer but other high-level languages will hide these details for you. Should we always use static dispatch for performance reasons, well, not quite. There are always tradeoffs, like the increase in binary size or even considering if this is worth it at all, maybe is not in a latency-sensitive area of your program or even a function that gets called sporadically.

Languages like Rust let you make the decision and give you control, but more importantly, makes you aware that this even exists. What about other high-level languages? Languages that have a virtual machine, like .Net, Java, JS... in this case, it might be different on each platform, the code that is written is not the code that gets executed, even more, the code that is executed at the beginning of the execution could be different than the code that will be run once the runtime has acquired more information about the context of execution. Things like dynamic PGO (Profile Guided Optimization) will make the machine make its own decisions based on data that is gathering during the execution, even go has introduced some PGO, so that you can feed this information on the next iteration of your application.

Compilers keep changing and not only for high-level languages, new instructions are being added to processors, so compilers learn how to take advantage of them and produce faster execution with the same code. PGO is becoming standard across performance-sensitive languages. Function inlining might revert some of these method's extractions at runtime. This means that code that relies on abstractions will have a different impact on different programming languages, even the same language can produce more performant executions of the same code in future versions of the compiler. There is a series of blogs, with many details about the speed of these improvements in the .Net platform, it's also worth highlighting the broadness of these changes. Java is also on a similar path nowadays, with the introduction of green threads by Project Loom, or some incoming changes like Project Lilliput or Valhalla among others.

All these make me value clean coding, as the maintainability of software is more directly impacted by the code that we write, but that doesn't mean that performance should be ignored, define performance budgets and ensure that the code you write meets the requirements.

Performance should not be ignored

On the other side of the spectrum, the arguments made by Casey Muratory and The Primeagen, or in a less dramatic voice, CodeOpinion, should not be ignored either.

Performance is money, this is increasingly becoming more important and transparent with the usage of the cloud, especially serverless architecture like lambda, where you pay by the second of execution time. Faster boot times and executions, together with lower resource usage have driven the growth in popularity of languages like Golang or Node over previously established platforms in the backend, like Java or .Net. Changes are being made to platforms to adapt to the new paradigms of how software is being run. Not only performance but speed to market due to a decrease in the complexity of designing scalable solutions.

There are some areas where performance clearly translates to money, like e-commerce. This is probably one of the areas with more studies where there is a clear representation between latency and the economic impact of customers leaving your platform for the competition, so performance matters, in certain industries with high competition might make your company succeed or fail. There are also other areas like health technologies or the automobile sector, where there is no room for surprises or long-stop the world events.

Over abstracting also has an impact on code clarity, too many abstractions and you end up losing count of how many tabs you need to perform a query to a DB. Make the methods that you expose easy to follow and free from low-level implementation details so that the main purpose of the execution can be digested easily. Looking at the folder structure on your application should tell you about the domain of the application, what business problems it solves, and who is the target audience or consumer.

Make performance a part of the conversation when designing software, don't leave until the end. Defining your performance budgets for sensitive areas of your applications, and optimizing your hot paths, might save you money or your company.

A balanced approach

Software design is a complex subject, just like economics, there are too many variables to produce and test economic theories, and there are too many variables in the hardware and software that we use to produce applications. Luckily for us, testing software is a bit easier and we can remove some of our bias with data. So let's define a set of guiding principles to help you take a pragmatic approach when you design your next piece of code:

  • Create interfaces that abstract the implementation between modules, but don't be afraid to couple and refactor classes in a module.
  • Focus on testability, and produce code that makes it easy to write tests and abstract inputs or outputs. Starting from your tests might help you design better software.
  • Benchmark your code and do performance testing on sensitive areas of your code, so that you can make informed decisions based on data, as the increasingly complex abstractions on hardware and software make it difficult to guess. Make performance testing a routine task, as this is also likely to change over time.
  • Monitor your production servers, focus on percentiles rather than average response times, so that you know what kind of service you are offering to the people consuming your services and see if that meets your expectations. Make it transparent to business leaders.
  • More likely than not, performance issues won't come for creating software that is easier to understand because a few methods or interfaces have been abstracted as many compilers/interpreters will revert some of these changes. Trust your benchmarks more than your code.
  • When there is a need to produce performance-sensitive code, try to gain a deeper understanding of the hardware, compiler and runtime optimizations where your code will be executed.
  • Design your solution for the team that is going to take care of it and maintain it, understand the capabilities and skills required to develop the solution and identify any gaps that might impact your decision.
  • Don't be afraid to tightly couple with a specific persistent service if you need to reach a certain performance. It will still be hidden by a clear and simple interface to use if you need to refactor it later. Abstractions like ORMs should not hinder your opportunity to optimize.

How to survive in a world of algorithms and AI

Jumping on the bandwagon of polemics for readers? if so, hopefully for the right reasons, performance vs clean architecture is just an example of the polarity in software design, monolith vs micro-services, and throw-away to rebuild, are some of those other extremes that we need less of.

With the growing need for more developers, we don't need any more reasons to demotivate them into taking a computer science career, AI is already taking care of that, where many tasks can now be delegated to AI instead of junior developers, but how can AI or junior developers become better at what their do if we don't seem to agree on what good code is. There are too many subtleties for extremism, pragmatism and good principles should reign instead.

I won't be waiting for the day when algorithms favor long-term retention and happiness of their users, as I'm not sure is even on the list of priorities dictated for a quick profit driven by the speculation of financial markets. I feel like the bigger the company is, the worse is their recommendation engine and the less relevant the principles of UX become. I will vote for dumbing down these algorithms in favor of a more straightforward approach where good content and user satisfaction are the leading metrics.

How difficult will it be to offer more content from my subscriptions? or show a survey to help select better videos based on my history to confirm the algorithm bias, like:

  • Do you prefer to watch more content from tech conferences?
  • Do you like more short-form news or long-form documentaries?
  • Should Google just pay for a Chat GPT license? Partly joking as some of their category classification on videos feels like they should.

For the moment I will keep with my current practices to avoid socially engineered content over quality:

  • Focus on tech conferences
  • Subscribe to people with consistently good quality
  • Use tools like PocketTube, which although not perfect, still improves the relevance of the content.
  • Use other sources, like programming books, in the case of software design, read something that has resisted the passing of time over temporary trends.

Let's make software design what it should be, a more integrated journey with the business and the people that are part of it.