Earlier quoted context omitted.
Please be specific about the issues and let’s discuss. I am serious - this list is optimized for maintainability of code. Developer time is more valuable than processor cycles, in most cases. Unless you are the kind of person who would argue that C++ introducing object orientation and virtual methods made everything slower and that extra indirection by default is hilariously bad architecture?
> 3. Functions should have extensibility, put the required parameters as parameters and always include an “options” at the end. Each function can have defaults that you can extend, which means you need a deep-extend method: counter-point: why not write functions that take the arguments they need? if they need more arguments later, why not add them later? > 4. When in doubt whether to do convention A or B, take a bit…
First of all, the last parameter should have a default value of {} — that is, no options, but can still be deferenced in code. So not passing options is ok.
Secondly, if you don’t do this, future developers will keep adding parameters in an ad-hoc manner until you get stuff like:
context.copyImage(src, dest, sx, sy, sw, sh, dx, dy, dw, dh, filter, rotation, matrix, ...)
and your calls will keep looking like:
context.copyImage(a, b, 0,0,30,30, 20,20,40,40, null, 5, null, “foo”)
Not only will it be harder to read for anyone looking at the calls, but also the future function signatures will have parameters in the chronological order they were added — which is often totally unrelated to the order they should be in, but you can only add them at the end.
Since the function should be backward compatible, all new parameters are by definition OPTIONAL and therefore can be added to a hash or object called “options”.
And YES I stand behind this. Years ago I actually recommended this to the PHP language mailing list:
https://grokbase.com/t/php/php-internals/1042mr8yrn/named-pa...
In other words, I wanted the function call syntax in PHP look like the array syntax:
func($a, $b, $c => 3, $d => “foo”);
Simple, and elegantly enforces the above convention while looking “quintessentially” PHP!
4. When you are building a re-usable framework, it’s silly NOT to anticipate future needs. The whole point of a framework is future needs.
Now, you say you should weite code rhat is easy to undeerstand and change. If you hardcofe vales, that’s easy to change - just put a variable there. But if you hardcofe a concention in 100 places, fhat’s not ao easy to change. You can’t just grep for a hardcofed constant.
But ot gets worse rhan that. Other code will come to rely on this “invariant”, which may change later. Again, the wholw goal of my recommendations is to future-proof your code so that future developers will write code rhat grows up around it and can go in any direction, and can play nice with each other.
5. Suppose you are lacking middleware between A and C. So now you want to mock an input from A. Too bad, you can’t. Ok, what if you want to modify something that goes on between A and C? You have to rewrite A or C.
Let me give a real example. Suppose I said that people may have one more articles they write. So I implement a User table and an Article table, which has authorUserId field on it. Simple, right?
Except it’s too simple - one article can at most one author. If instead I had thought ahead and made three tables: Author, Article, and Authorship as a join table with articleId, authorId, then I could have 1 to N mappings in both directions and far more flexibility.
Now you may say — why think ahead? Maybe in the future articles can have more authors and THEN we will refactor the code! Except at that point you’ll have tons of plugins and apps, some beyond your control to change, relying on the details of your implementation.
Of course, you should also use another principle I didn’t mention (because it’s very obvious and popular) namely to write abstract interfaces that don’t leak implementation details, and keep these interfaces as small as possible, so that you can reason about large systems through these “bottlenecks”. But I have found that, on the back-end, it’s just a bit of extra work to add an extra indirection, whether you use it now or not. Instead of saying “we will NEVER do it the other way because it makes no sense”, if it costs you so little, why not allow for it, in case later someone will want it? The interfaces are often a leaky enough abstraction for this to matter.
For example you’d have article.getAuthor() if you didn’t make that extra table join indirection. And now what will article.getAuthor() return when articles can have many authors? It would return a random author, for backward compatibility. With my approach, you would prevent the “older” apps from using dumber interfaces. It’s just a bit extra work for everyone, for huge wins later. And that’s the point.
7. Event streams can be abstracted into pipelines and middleware. You can do undo/redo, store histories, have Merkle Trees and more. Compare SVN and Git :)
8. Sync becomes much easier when everything has a history of states. Look at git. You can just use it. Look at scuttlebutt, blockchains, or other types of merkle trees. Everything becomes super simple to reason about.
This forum could be refactored to be distributed. Everyone owns their own node of the tree and the relationship to its children (replies), and everyone else just replicates them scuttlebutt-style. Expanding a tree is fairly simple, and each branch has a merkle hash. There is no central server.
And the best part - you could start with a centralized app and gradually move to a decentralized, end-to-end encrypted model, if you only had the foresight to make sure that your tables has primary keys corresponding to how people look up some data (node, etc.)
The only thing you’d need to have consensus about is the ordering of replies to a node. And that consensus can be among the repliers or simply dictated by the parent node’s owner.
8. Having a non-extractable private/public key be used to sign requests is better than JUST having a bearer token (cookie). If someone commandeers the cookie via, say, session fixation, they still won’t have your private key. But they need the session id (bearer token) to look things up on the server. This is “piling on”.
Then, on top of this, you can have a blockchain of keys for devices, stored across sites, so you can revoke a device when it’s compromised, or authorize some new device with N previous devices.
You can have the same exact mechanism manage users in a chatroom or other merkle tree structure. This is what keybase does.
You can encrypt data on the server, with people’s public key, and they have to decrypt it.
You have to make sure that the initial signup requires some sort of token to prevent sybil attacks.
In short — once again the approach is to “layer on more security mechanisms”, they should all work independently.
You don’t just rely on HTTPS for example, because a server or CA cert can be compromised. You hash passwords on the client with salts before sending, regardless. Once again this is called “defense in depth”.