Strict vs. Flexible Code

Cover Image for Strict vs. Flexible Code

TLDR;

Strict code, which is rigid about inputs and crashes on errors, helps identify and fix issues faster, ultimately leading to better software. Flexible code, which adapts to errors, often results in hidden bugs and maintenance challenges. While strictness is ideal, finding a balance that fits the scenario is key, particularly when it comes to web development.

Introduction

While learning to code, I was taught to think of all scenarios and to handle all potential outcomes elegantly; to foresee all the ways in which the code may veer from the happy path, and to code defensively to prevent unforeseen issues. I used to think that it was better to write code that was flexible and permissive about its inputs. I thought that this would make it easier to build software that could handle a wide variety of inputs and use cases.

As I began working with enterprise software, I carried that ethos with me, but it slowed me down during the initial development phase. Defensive programming often demands more thought and time, and in many scenarios, spending those extra resources is not always appropriate or even possible at all. I have come to realize that it is better to write code that is strict and inflexible about its inputs. This makes it easier to diagnose and fix errors, and ultimately build better software.

However, I have also come to realize that there is a fine balance of flexibility vs. inflexibility.

The Parable of Mike and the Login Shell

This parable stuck with me ever since I read it:

Once, a student named Mike wondered whether it was better for programs to be written so that:

  1. Each function would be strict about its preconditions, checking its inputs and crashing immediately with an assertion failure if a precondition was violated; or
  2. Each function would be permissive about its preconditions, checking its inputs where necessary, but repairing erroneous inputs and proceeding as best it could.

So, he wrote two Unix shells: one in the strict style, and one in the permissive style.

The shell written in the strict style would crash, at first. Mike was fearless enough to use his work-in-progress as his login shell; crashing was incredibly inconvenient, as it would log him out of the machine completely. Nevertheless, he persisted; he found and fixed defects at a rapid rate, and soon enough the shell became a usable and useful tool.

The shell written in the permissive style also had defects. But he was never able to find and fix enough of them to make it usable. Eventually he gave up on this shell.

He concluded that it was better for most programs to be written in a strict and crashing style. Even when crashing was incredibly inconvenient, it made errors so much easier to diagnose and fix that you could build better software if you did it. Mike went on to become one of the eminent programmers of his generation, earning fame and fortune.

A Counter Example

I'm often amazed at how often I encounter forms on the web that don't trim extra space characters on inputs (especially common on mobile devices which tend to be quite liberal in appending a space at the end of an input).

String Trimming error

Extra spacing often triggers errors when submitting forms, and the error messages are often too vague to pinpoint what the actual problem is with the form input. Not to mention that it can be tricky to spot extra spacing in inputs, especially for non-technical users.

On one end of the spectrum, the inflexible approach would be to reject inputs which contain extra spaces, but to also adequately signal to the user that such inputs are not valid. On the other end of the spectrum, a developer would simply trim the inputs before processing the data. In this case, the flexible approach seems more appropriate for a web form, whereas the inflexible approach would perhaps be more appropriate for a CLI-based application.

Waves Analogy

Let's discuss an analogy when it comes to the practical side of a flexible vs. inflexible approach.

Imagine driving a cigarette boat across a lake as fast and straight as possible. You will get to the other side very quickly, but you will create large waves. Now imagine doing the same in a kayak. As compared to the cigarette boat, it is much slower, more maneuverable, and creates virtually no Waves. The waves in this analogy refer to churn and second, third, and n-order effects as a result of pushing the code. In this case, unexpected bugs / issues discovered in QA or production, which force you to drop everything and debug / fix them.

Happy Path crying developer

"Outsourcing" Development

You can outsource a significant portion of your programming work to QAs and the general public. Do this by driving the cigarette boat. Code the happy path quickly, push, and move on. Let other people do the work of testing and finding issues for you (contending with the waves that you have created) over a period of time which elapses as you are working on other things.

If a critical issue is found, you'll find out about it. You might pay dearly for doing this, but sometimes it's worth it and even necessary. In a certain sense, all devs do this anyway, given that some issues only present themselves in production.

The Happy Medium

The happy medium is to write code which is tastefully defensive, and to ensure that you have robust observability tools in place to alert you of performance issues, bugs, etc. This allows you to spot issues in real time and to be prepared for when your clients / end users start running after you with pitchforks and torches.

Pitchforks and Torches

Conclusion

Ultimately, this is a question of whether a programmer should err on the side of letting things fail vs. not. The experienced programmer is aware of this dynamic, takes easy wins in terms of defensive programming, and adjusts their programming style to fit the scenario.

Be water,

-MG


More Stories

Cover Image for On Sitecore Stack Exchange (SSE)

On Sitecore Stack Exchange (SSE)

> What I've learned, what I see, what I want to see

Cover Image for Year in Review: 2022

Year in Review: 2022

> Full steam ahead

Cover Image for Add TypeScript Type Checks to RouteData fields

Add TypeScript Type Checks to RouteData fields

> Inspired by error: Conversion of type may be a mistake because neither type sufficiently overlaps with the other.

Cover Image for Hello World

Hello World

> Welcome to the show

Cover Image for Content Editor Search Bar Not Working

Content Editor Search Bar Not Working

> Sometimes it works, sometimes not

Cover Image for Tips for Forms Implementations

Tips for Forms Implementations

> And other pro tips

Cover Image for SPE Script Performance & Troubleshooting

SPE Script Performance & Troubleshooting

> Script never ends or runs too slow? Get in here.

Cover Image for Security Series: App Service IP Restrictions

Security Series: App Service IP Restrictions

> How to manage IP rules "at scale" using the Azure CLI

Cover Image for Sitecore Symposium 2022

Sitecore Symposium 2022

> What I'm Watching 👀

Cover Image for Don't Ignore the HttpRequestValidationException

Don't Ignore the HttpRequestValidationException

> Doing so could be... potentially dangerous

Cover Image for On Mentorship and Community Contributions

On Mentorship and Community Contributions

> Reflections and what I learned as an MVP mentor

Cover Image for Early Returns in React Components

Early Returns in React Components

> When and how should you return early in a React component?

Cover Image for Script: Boost SIF Certificate Expiry Days

Script: Boost SIF Certificate Expiry Days

> One simple script that definitely won't delete your system32 folder

Cover Image for NextJS: Short URL for Viewing Layout Service Response

NextJS: Short URL for Viewing Layout Service Response

> Because the default URL is 2long4me

Cover Image for Troubleshooting 502 Responses in Azure App Services

Troubleshooting 502 Responses in Azure App Services

> App Services don't support all libraries

Cover Image for Ideas For Docker up.ps1 Scripts

Ideas For Docker up.ps1 Scripts

> Because Docker can be brittle

Cover Image for Tips for New Sitecore Developers

Tips for New Sitecore Developers

> If I had more time, I would have written a shorter letter

Cover Image for Critical Security Bulletin SC2024-001-619349 Announced

Critical Security Bulletin SC2024-001-619349 Announced

> And other scintillating commentary

Cover Image for JSS: Reducing Bloat in Multilist Field Serialization

JSS: Reducing Bloat in Multilist Field Serialization

> Because: performance, security, and error-avoidance

Cover Image for Tips for Applying Cumulative Sitecore XM/XP Patches and Hotfixes

Tips for Applying Cumulative Sitecore XM/XP Patches and Hotfixes

> It's probably time to overhaul your processes

Cover Image for NextJS: Access has been blocked by CORS policy

NextJS: Access has been blocked by CORS policy

> CORS is almost as much of a nuisance as GDPR popups

Cover Image for JSS + TypeScript Sitecore Project Tips

JSS + TypeScript Sitecore Project Tips

> New tech, new challenges

Cover Image for Super Fast Project Builds with Visual Studio Publish

Super Fast Project Builds with Visual Studio Publish

> For when solution builds take too long

Cover Image for NextJS: Unable to Verify the First Certificate

NextJS: Unable to Verify the First Certificate

> UNABLE_TO_VERIFY_LEAF_SIGNATURE

Cover Image for On Sitecore Development

On Sitecore Development

> Broadly speaking

Cover Image for How to Run Old Versions of Solr in a Docker Container

How to Run Old Versions of Solr in a Docker Container

> Please don't make me install another version of Solr on my local...

Cover Image for Azure PaaS Cache Optimization

Azure PaaS Cache Optimization

> App Services benefit greatly from proper configuration

Cover Image for NextJS/JSS Edit Frames Before JSS v21.1.0

NextJS/JSS Edit Frames Before JSS v21.1.0

> It is possible. We have the technology.

Cover Image for Symposium 2022 Reflections

Symposium 2022 Reflections

> Sitecore is making big changes