They don't understand that I'm not wasting time, I'm just choosing to spend a little bit of time earlier, because I don't like spending a lot of time later when debugging why everything broke.
They don't understand that I'm not wasting time, I'm just choosing to spend a little bit of time earlier, because I don't like spending a lot of time later when debugging why everything broke.
With buggy ship-it-now software you have a known bounded risk - bugs will occur in some cases but the software will ship and the bugs can be fixed because it’s simple.
With prematurely architected software the risk is unbounded - the project may get bogged down indefinitely in its own complexity without shipping.
The inverse extreme can also be a problem, of course A project that is maintained for a long time on the naive implementation will also become unmaintainable. However, this will be due to _known_ architecture problems encountered during maintenance. These problems can be addressed in a relatively bounded amount of time. They are also quantifiable and thus explainable to management.
Though, given the constant incentive to never learn how to write a proper parser does mean that practically nobody knows how to do it, and so it will always look & feel like an unbounded science project, and thus rarely done.
Infinitely recursive MVP'ing is a race to the bottom.
https://www.aarthiandsriram.com/p/our-dream-conversation-and...
``` Turbo Pascal 2 and Hash Tables
Sriram: I think there is some, we're going to talk later about things like GitHub co-pilot, we're dealing with different kinds of code reuse, but I do think there's a little bit of romance in the, for example, I idol you and folks like John Carmack. John Carmack noodling away and trying to make sure every instruction and memory access is aligned on some cash line. The amazing thing is, even today, if you look at AI at the heart of it, you have matrix multiplication and doing things with floating point numbers. Some of these things really tend to matter, which on the theme of optimization, tell us a story of Turbo Pascal 2 and you discovering hash tables because it's legendary.
Anders: [laughs] It is a funny story. You have to remember here that I am self-taught in the art of writing compilers. Now, I have since discovered and read a lot of the literature, but at the time, I was just coding away and doing it the best way I knew how. When you create a compiler, one of the things you have to create is simple tables, or they're not necessarily tables. They could be linked list, they could be whatever, but you have to look up names. When you're trying to compile a code that tries to assign one to X, well, you have to figure out where is X? What memory address have I associated with X? You have to look it up the declaration of X.
In Turbo Pascal 1.0, the first version of Turbo Pascal, all of the variable declarations were just kept on a linked list because that much I knew, but of course, searching linked lists is not particularly efficient when they get large. For really large functions, that would just take an awfully long time. Then, well, maybe you could try first go by first letter, and then have 26 linked lists or whatever, and that could help a little bit. Then I remember reading the literature about these things called hash tables actually in this book by Niklaus Wirth, Algorithms +, what is it? Yes, Algorithms + Data Structures = Programs.
A great book. He explains hash tables and I go, "Oh my God, that's amazing. I got to go try this." I went and implemented it and boom, the compiler went twice as fast. There's Turbo Pascal version 2.0.
[laughter]
Aarthi: Awesome.
Anders: There's a good reason to sell upgrades. ```
Do you really need a proper parser to sell 1.0? A literal compiler project did not fail due to the lack of a "proper" parser (though it can be argued that the lookup table is after the parsing stage; obviously there was not a strong distinction in the Turbo Pascal implementation between tokenization and compilation).
How would be the cost of bugs be known and bounded? A race condition in the code might not bother anyone ever in practice, or it might break down the whole system right as the most important investor decides to dogfood the product. In one case the bug can cause zero harm, and in the other might ruin the whole company.
How much was the "known and bounded risk" of shipping a buggy transactional model to the British Post Office? [1] It is "just" a buggy database, yet it cost many lives and sent many people to prison wrongfully.
> the bugs can be fixed because it’s simple.
Maybe? Have you tried in practice? Sometimes a buggy implementation forces you into a pathway where you need to spend inordinate amount of effort to get to a non-buggy solution. (This is the idea behind the term "technical debt".) Very often this effort is more than what you would need to do to just do it correctly the first time.
1: https://en.wikipedia.org/wiki/British_Post_Office_scandal
There are _lots_ of good practices which reduce the (potential) bug count without making things more complicated -- indeed, good practices serve to reduce complexity in design and architecture. Selecting the right data structures and algorithms, server-side validation, making proper use of your language's type system, choosing the correct type of database, writing some unit/integration/system tests, use caching judiciously, I could go on.
It is abundantly clear that GP is roughly describing two ends of a spectrum, not enumerating every possible option.
The other big one I constantly fight have to fight for is using type annotations in Python. It's a total no brainer and definitely not an extreme end of the spectrum. I'm not advocating formal software verification.
There is EXACT SAME PROBLEM with choosing to cut engineering tho. And the cost of getting it wrong is far higher.
If you catch a problem early, well you just wasted the amount of time you tried to save.
If QA catches the problem before you get it to the client, that's 2-3x the cost right there. 5-10x when it gets caught on production. 20 to near unbound when it causes some long standing issue that's noticed too late
>With buggy ship-it-now software you have a known bounded risk - bugs will occur in some cases but the software will ship and the bugs can be fixed because it’s simple.
>With prematurely architected software the risk is unbounded - the project may get bogged down indefinitely in its own complexity without shipping.
Not every software is a startup. If you have a requirements, first verify that they make sense in wider scope, then implement carefully while caring about potential edge cases. Not even handling them just making sure that they error out instead of propagate potentially invalid data further. That doesn't cost you even 2x the productivity.
> The inverse extreme can also be a problem, of course A project that is maintained for a long time on the naive implementation will also become unmaintainable. However, this will be due to _known_ architecture problems encountered during maintenance. These problems can be addressed in a relatively bounded amount of time. They are also quantifiable and thus explainable to management.
In vast majority of cases those end up as pieces of spaghetti legacy code that are worked around by the rest of the org, not fixed. "Fixing it later" is more and more and more expensive the longer it is in production and the more code grows around it.
Shoddy code only happens in "the production is on actual fire, not manager-imagined fire" fixes and not for long.