Captain Hindsight

Started by CultLeader, September 16, 2021, 03:23:32 PM

Previous topic - Next topic


Today I want to talk the most foolproof and rock solid way to predict the future - the hindsight.

It may sound obvious, but if something is obvious, has obvious advantages and yet not many people use it and believe the benefits of it, maybe it is not that obvious?

Today a lot of software developers start building complex abstractions, especially in OOP, without having a clue what practical needs actually are. I'm talking mostly about Java projects, which abuse tons of interfaces like I mentioned in specific vs generic thesis.

The problem is not wanting to abstract something to high levels. The wish itself is amazing and is core principle of the pattern which solves tons of problems that cannot be solved by 99% software engineers today.

The problem is that people miss the abstractions, they build them without hindsight of do's and dont's to arrive at the simplest possible solution to the problem. What is inevitable, that by spending time on board and designing something that looks good in imagination yet is quite a flop in reality, because they don't have hindsight.

How to build the abstractions perfectly by acquiring the hindsight?

Do not build the abstractions right from the start. Before I had the pattern which allows me to solve any programming problem in the most concise elegant and most efficient way I didn't have abstractions.

Say, today, I define typesafe rest endpoints and frontend code gets generated and I implement typesafe functions in the backend. How did I think about that? In hindsight, it is obvious. But to arrive at this conclusion, as a poor soul that I was, I simply wrote OCaml code for the backend, and I separately wrote OCaml code to the frontend. I parsed url arguments by hand, was doing mistakes and I just thought to myself - this is bothersome and error prone work. Why can't we generate code that calls the frontend, parses typesafe responses and you don't deal with json at all?

So I did, I did not have this powerful abstraction at the start. I was doing that work by hand, then there was actual, practical need for the abstraction and then I built it and enjoyed the benefits ever since.

Same happened to me when interacting with postgres. Yes, OCaml is amazing, typesafe, as long as you work in its typesafe plane. But, if you use database, you send a string which is not typechecked to the database, hence, you must run the code that uses database to know it will work. So, I had OCaml, that is wonderful language, yet I am suffering from pitfalls of dynamically typed languages I cannot stand by using plain SQL in OCaml code. I didn't want to write tons of tests either like ruby nutjobs do.

Again, from this practical need, I decided to solve this problem with the pattern, every interaction with database via SQL queries must be defined as data and ran against test data with test arguments to ensure query succeeds and returns results. Not only that, I went step further, I didn't restrict functions that were to be generated to just return bunch of strings you have to parse - I made sure all the results from generated code were parsed into typesafe OCaml types.

So then, I brought database into OCaml world, I write SQL queries that are checked in meta executable for correctness and generate typesafe functions that accept typesafe labeled arguments and also return typesafe results of array of structs. From OCaml standpoint, I completely eliminated risks of using raw SQL strings in OCaml and I am in wonderful typesafe OCaml land again.

The need for both of these abstractions arose from hindsight. Not to end up like Java projects, I NEVER BUILD ABSTRACTIONS BEFORE I KNOW I PRACTICALLY NEED THEM. I do the work by hand in the start, once I see I copy paste a lot of stuff that is mostly repeating, at that point I know what I can abstract and what code I can generate and hence I can never make mistake of producing useless unneeded abstractions.

Or, think of Java vs C# - Java was the first one to make all the mistakes and C# did everything better. Or think of C++ vs Rust - Rust had tons of hindsight about bad practices of C++ and eliminated them.

It doesn't matter whose hindsight you use, actually, to use someone elses mistakes to your advantage is cheaper. But, with things like the pattern, as I am the only person I know who does this, and I cannot understand why everyone doesn't do this, I have no hindsight of others to work on. Hence, in such cases, I have to make mistakes myself and then, with hindsight, turn those into abstractions that actually work.

I do not pretend to know everything about good practices with the pattern, I am the first person I know to use it, hence, the area of the pattern is an utterly uncharter experimental territory that needs lots of exploration to be made, and why I created this forum, to help find more fellow pioneers. Am I scared? Not really. You see, everyone else around me is riding a horse and I am the first person to have the very first car. Will it have its quirks? Sure, but I'm already ahead of everyone else that has a horse.

So, long story short, if you want to make practical abstractions, that will be joy to use and will have no risk of becoming overcomplicated, overengineered or useless - always base your abstractions on hindsight, be it someone elses or yours. Don't be an idiot who reads whitepapers all day written by people without practical sense thinking you'll do yourself a favour if you implement something purely theoretical and not based on practical hindsight.

Peace bois.