In the original ANSI C as described by K&R, there are a bunch of library functions that use internal state static variables. An example is
strtok, which you call with a string to tokenize, and when you want the next token, you call it with
strtok uses internal static variables to keep track of the string and an iterator in it.
In early C usage, this was fine. You had to hope that any 3rd party library call you made while iterating tokens weren’t also using
But, then when threads were introduced to UNIX and C, this broke down fast. Now, your algorithms couldn’t live in background threads if they used
strtok. This specific problem was solved with thread-local variables, but the pervasive use of global state inside of C-functions was a constant source of issues in the most multi-threading and post multi-processor world.
And the world was changing from delivering desktop apps to web apps, so now a lot of your code lived in a multi-threaded back-end that serviced simultaneous requests. This was a problem because in early web development, we took C-libraries out of our desktop apps and made them work in CGI executables or NSAPI/ISAPI web-server extensions (similar to Apache
To make this work, we had to use third-party memory allocation libraries because the standard
delete implementations slowed down as you added more processors (from constant lock contention). Standard reference-counting implementations used normal
-- which aren’t thread-safe, and so we needed to buy a source code implementation of
stl that we could alter to use
InterlockedDecrement (which are atomic, lock-free, and thread-safe).
As the world changed around us, we could keep moving forward with these tech-debt payments.
Also, this was slow-paced problem—
malloc/etc were written in the 70s and limped through the 90s. That’s actually not that bad.
But, the world doesn’t stop. Pretty soon, it was just too weird to implement back-ends as ISAPI extensions. So, you pick Java/SOAP because CORBA is just nuts, and well, that’s wrong because REST deprecates that, and then GraphQL deprecates that, and you picked Java, but were you supposed to wait for node/npm? Never mind what’s going on on the front-end as JS and CSS frameworks replace each other every 6 months. Even if you are happy with your choice, are you keeping your dependencies up to date, even through the major revisions that don’t follow Substitutable Versioning?
And I think that this is the main source of tech debt, not intentional debt that you take on or the debt you accumulate from cutting corners due to time constraints. The debt that comes with dependency and environment changes.
Being able to bring code into your project or build on a framework is probably the only thing that makes modern programming possible, but like mortgages, they come with constant interest payments and a looming balloon payment at some point.
There are some you can’t avoid, like the OS, language, and probably database, but as you go down the dependency list, remember to factor in the debt they inevitably bring with them.