In the original ANSI C, there are a bunch of library functions that use internal static variables to keep track of state. For example, strtok
is a function that tokenizes strings. You call it once with the string, and then you call it with NULL
over and over until you finish reading all of the tokens. To do this, strtok
uses static variables to keep track of the string and an iterator into it. In early C usage, this was fine. You had to hope that any 3rd party library calls you made while iterating tokens weren’t also using strtok
because there could only be one iterator at a time.
But 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 when multi-threading became mainstream.
The world was switching from 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 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 mod_
extensions)
To make this work, we had to use third-party memory allocation libraries because the standard malloc
/free
/new
/delete
implementations slowed down as you added more processors (from constant lock contention). Standard reference-counting implementations used normal ++
and --
which aren’t thread-safe, and so we needed to buy a source code implementation of stl
that we could alter to use InterlockedIncrement
/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—strtok
/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.