1
0

Patrick.net Coding Conventions


 invite response                
2024 Sep 18, 4:47pm   100 views  3 comments

by Patrick   ➕follow (60)   💰tip   ignore  

I was telling @mell that when I retired, I decided to code my own websites (https://patrick.net and https://webfam.net/) exactly the way I always wanted to, and not according to rules which never made sense to me anyway, but which I had to follow in various jobs.

Each path or "route" on the websites corresponds 1:1 with a function. The path is the first "word" after the domain name, like "post" This allows us to instantly know what function any web page is generated by. So for example, on patrick.net the url https://patrick.net/post/1382101/2024-09-18-polls-betting-markets-and-election is generated by the post() function:

routes.GET.post = async function(context) { // show a single thread and its comments, logged in or not

So I have a "routes" hash with two parts, GET and POST. Within those two parts are all the possible paths. The rest of the paths are things like the post_id (eg 1382101) and a descriptive but optional tag like 2024-09-18-polls-betting-markets-and-election which has the post_date and slugified title.

So when you get that post, the routes.GET.post() function queries the database to get the post content. There is a "posts" table in the database, with the post_id, post_title, post_author, etc. Every database table is a plural noun, and every column starts with the singular version of that noun, like post_id.

The point of that is so that I can read from the database into a variable whose name reflects the table the data came from. As with the routes, it's a big time-saver in debugging. I can just glance at a variable and know what table its value came from.

Tables and columns are always lowercase. Fuck uppercase unless there's a good reason for it. (GET and POST are uppercase because those are literally the exact way those verbs are sent by the browser.)

No functions ever modify the values of their parameters, making them so-called "pure functions". This helps in debugging. All mutable state is in the database alone. So I can be certain that the same code against the same database state will always display the same thing. Minor exception is timestamps, like when you see "Posted 6 minutes ago".

There is exactly one node.js file for the entire site, and that includes client-side js and css. Any included javascript or css is stored as close as possible to where it is served in the code. This not only makes the site considerably faster than it would be if it had to include external .js or .css files, but it again helps with debugging because you don't have to go hunting in other files.

An even bigger benefit to one file for the whole site including all js and css is that there is no possible version mismatch between the node code and the js or css. This makes deployment way easier. Just deploy the one file which has everything. Caching of js and css is actually a big performance loss, not a win. Including only what js or css the current page needs is incredibly fast by comparison. There are of course no javascript frameworks like React which take a million years to load and are minefields of complexity.

There are no global variables, though there are a few global constants which are read from a one-row configs table on startup. Every variable that can be coded as a const is const. Every row read from the database is frozen and may not be modified in the code, but only via a database update statement.

There is no compilation of CSS or obfuscation of javascript. It's all vanilla, so the change and deploy cycle is incredibly simple and fast: change code on laptop, look at it in browser on laptop, commit to git (which runs the tests), rsync the one file with everything to the server. Commit to git fails if the any test fails.

I created no node modules myself, but do import some of them via npm via "require()". I almost never upgrade them, the only exceptions being a known serious security hole or an incompatibility with a different module I need.

Tabs are forbidden and indentation is always 4 spaces. Fuck tabs. CamelCase is hideous. Fuck that too. All names that I can choose are lowercase_with_underscores for readability and to avoid problems with dash being interpreted as a minus sign in class names.

There are no single letter identifiers. They are confusing because they are essentially meaningless.

No identifier may be a substring of any other identifier, so full-text substitutions in the code always work perfectly.

I use ALE in vim to check my code as I type. My ALE rules are in .eslintrc.js and enforce conventions such as never having a local variable of the same name as one at a higher stack level.

vim is set to color-code everything on a black background, making it instantly clear what type of thing every word is: function, keyword, variable, etc.

All unhandled exceptions on the server result in an email to me personally, so I usually know if the site is misbehaving before anyone else does. The email includes the http request data, the stack trace, and other useful info.

All unhanded exceptions in the javascript in the browser also result in an email to me.

HTML is generated via a series of nested functions like this:



This makes it very easy to duplicate, eliminate, or swap around parts of pages. It's nice that function calls can be hierarchical like html, so you can map one to the other like I do.

There is a "context" hash passed down as a parameter from the top level function handing that path so that all subordinate functions can get the current user info, etc.

The server is monitored and restarted with pm2, which runs two processes so that deployments result in zero downtime. One process is updated and then the other. I have a bunch of other monitors, like db connections and disk space, which I view on a Grafana dashboard.

Testing is done with mocha, with at least one test for each route. Tests must all pass before the code can be committed to git.

In debug mode (a startup parameter) the site prints all db calls and their timings to the console. This helps track down slow queries.

No objects beyond hashes are used. Fuck OO programming. It sucks and provides less than zero benefit. There are only functions and their parameters, which may be hashes.

There's a bunch more, but I don't want to bore you.

Comments 1 - 3 of 3        Search these comments

1   mell   2024 Sep 18, 5:57pm  

I think OO in JS/TS is just poorly implemented, it has its use cases, but for your website there aren't any. One or many exported functions packaged in a module/namespace is not much different from a class. Also Maps and Sets are nice when needed.

JS/TS has a nice middle ground between crazy OO and crazy immutable!functions!only!with!no!side!effects!and!no!procedural!code! purism crap you often see in Scala or other fp leaning languages.
2   Patrick   2024 Sep 18, 7:16pm  

I did a bit of C++ and hated that as well. Those are my two OO experiences. I admit javascript OO is poorly implemented. It just grew there, like a fungus.
3   HeadSet   2024 Sep 19, 1:56pm  

Patrick says

Those are my two OO experiences.

Microsoft is revising Basic to have object oriented binaries, called BOOB.

Please register to comment:

api   best comments   contact   latest images   memes   one year ago   random   suggestions   gaiste