4 • 29 • 23 natesymer
Hey y'all, it's been a minute.
I was off completing my computer science B.S. (which, is a load of BS but that's another story). The positive was that I had the opportunity to pick my tech stack for once (fuck yeah!) during my capstone class (they ported doctoral/masters thesis to undergrad, fuck yeah!), and I decided I wanted to do something new to me: NodeJS. As someone with experience with other backend shit like Django, Laravel, Rails, etc, I wanted to expand my horizons. And expand I did.
I was hearing all sorts of chatter about ReactJS server-side rendering, and how it required either running a node process to run the rendering, or, more optimally, just writing your backend in JS with node & some framework like express. I figured I'd check it out. Spoiler: It's fucking lame & a total waste of time. Write faster frontend code instead, it saves you tons of time chasing down bugs that happened because the SSR environment is slightly different than the five browsers you're already trying to support.
Anyway, NodeJS. JS is a fantastic language, and having used it a whole ton for browser code, I got hard thinking about using it for server code. After several months of working with it, here is my review.
The coding experience
JS's newness is its biggest positive, and its biggest negative too. It has a lot less cruft than other languages, and offers some of the most forward-thinking features of any language outside of Haskell et al. It's also great that you don't have to switch between languages on frontend vs backend, AND you can share code (like schemas and validation - how many times have you wanted to do that?) between backend and frontend. There is very little repetition, and everything ends up working really seamlessly. Well...
There is no ORM like Django's or even laravel's. I used Sequelize, and while it kinda did the job, it was a pain in the ass to use. You either had to use a destructive, not-for-production system to update your database's tables (the sync method), or you had to write your own migrations to reflect model changes. I shouldn't have to tell you that's not only repetition, but repetition in a place that's both prone to bugs and hard to debug (or even identify as the problem!). Things like this are legion in JavaScript land. It feels like it was all created by a bunch of amateurs.
In fact, everywhere I turned, I found myself having to fill in the gaps left by others. No sane solution for autogenerating migrations? Write MoldyMeat to automagically generate migrations as needed (with a little developer input, similar to Django's ORM). No library like watchgod? Write it yourself. Many backendy things are conspicuously absent.
Takeaway
Would I use NodeJS for a backend again? I'd like to because it's seamless, but the lack of backend libraries out there makes me hesitate. Check back in a couple years after people (probably including me) write them.
Migrations
Remember that pain point that Sequelize doesn't have any real/accepted way to autogenerate migrations when you change your models? It's something both Django and Laravel can do, so why does it not exist in JavaScriptLand? It's such a headscratcher that I asked myself if I was having a smooth brain moment or that there really wasn't a fully featured ORM for JavaScript (you know, that's as convenient as Django's is).
Further, why the fuck are we still doing migrations? I mean, look at this shit (django migration docs). Updating your database structure to match your models shouldn't require a PhD in Documentation-Fu, nor should anything but your model definitions drive how your database's tables are structured. Frameworks should figure it out for themselves (like an adult!) and ask the developer if they need guidance. I mean, I am being rational here, right? haha
MoldyMeat
Meet MoldyMeat, your new best friend and/or lover. MoldyMeat works like this:
- Loads the last known database state from the database
- Diffs it with the current model definitions (
sequelizeInstance.models
)- If running in development mode, prompts the developer to generate hints & stores them in the codebase
- Performs a sequence of database operations to make the database match the model definitions based off the diff and hints
- Adds a new database state record that reflects the current state of the database
- If any hints were used, record that they were used to reach the current database state that was just stored.
This process can be ran in both directions, forward & backward. The only difference between going forward and rolling back is the order of arguments passed to the diffing function!
Great, so how is this better/different from migrations?
Besides being 300% more awesome, moldymeat differs from migrations in that only ambiguous changes to the database are recorded in files intended to be committed to the codebase/scm. Say you renamed a model's attribute in your model definition; That would diff as:
- deleting the old field
- adding a new field
If you were running MoldyMeat in development mode, you'd be prompted/asked if this was a rename. Answering yes would cause MoldyMeat to generate a hint, which would be committed to SCM. Answering no wouldn't generate a hint, and would let MoldyMeat do it's thing unencumbered. The idea is that MoldyMeat has a given way of working that's reminiscent of Sequelize's sync()
function, but when given more information can avoid causing data loss.
Hints would contain all relevant table/column information, as well as the time at which the hint was created. If you were running moldymeat against a DB, it would know only to use hints created after the most recent database state's createdAt
date.
The best part? Hints are not code - they're data/artifacts. In fact, you can delete them if every database you work with has been brought up to the most recent model state (i.e. once everyone on your team runs them locally & your team lead runs them in production). Yeah, I know you can "flatten" migrations, but that requires connecting to everyone's database (as well as production databases too) to do so & as such, requires a lot of coordination & and is often seen as something to put off as long as there aren't 100s of migrations (because it's a huge waste of time and a pain in the ass).
Finally, you can write your own hints, including ones with raw sql (one statement to run the hint forward, one to reverse it; similar to a migration).