React, MobX, Apollo, GraphQL

I have been using React for a good 2.5 years now, and I'm still very satisfied with it, really not much to add here. I did some dabbling with Angular (1), and it felt like a "wrong road" compared to React. I did a project with MobX, and it felt great in the beginning, but when an error happened, the stack traces it provided was as useless as Node's, it was really hard to figure out what is causing a reaction to fire and fail. Still, I'd give it another go.

GraphQL is a novel idea, and it seems to work out for me so far. ut there are definitely things that are much easier to express with GraphQL eg.:

  • Pagination for entity relationships
  • "Mashup" endpoints, where you need multiple entities
  • Overfetching is not a thing
  • Self documented API through introspection
  • Typing

One thing I don't buy into is that you no longer need versioning. This is true in the sense that you will not need to version the endpoint(s), but the burden of maintaining multiple version of an entity/property, is still going to be there. The added typing is really nice for catching errors earlier, and having an auto generated documentation about the entities is really nice and helpful. Using something like Apollo playground makes it really easy to do some quick tests while developing. I'm not sure what client libraries are out apart from Apollo client, so I can't really compare it to anything else, but so far it seems to work out as well. The cache handling is a bit bare bones, the API for it feels very low level, I always have to drop into a debugger, or put console.logs in my cache updates to figure out why a write to a cache is failing.

ReasonML & ReasonReact

I seriously considered writing a frontend application in ReasonML, but in the end I dropped it. Their sell of "if it compiles, it works" is really true, at least it was during my testing. I modeled some parts of a board game called "Azul" in it, and if my code compiled, it 99% did what I was expecting it to do. No runtime errors, no unexpected results. I think it's already in a good enough shape for writing individual components, and integrating those into a larger Javascript project, but I would not use it in a full project until some major things are addressed, such as:

How do you do server side rendering? You would probably use OCaml on the server somehow?

It needs some kind of global state handling, because it's completely lacking. There is no redux/mobx, even the React Context API is missing. You will have to pass callbacks everywhere, multiple levels deep. I don't buy that this gives you clearer and easier to reason about code, it just feels like a hassle.

I did not think there could be a worse standard library than Javascript's, but yeah, the ReasonML one is also next-to-nothing. There is a third party library called "Belt" that adds some utility functions though.

NodeJS

This one makes my blood boil. There is no other programming environment that is more hostile towards developers trying to debug code, than NodeJS. Any code that uses promises, or async/await will produce stacktraces that are completely useless. The relevant lines of the stack trace are omitted, the only frame that shows up is the single line where the error was triggered. If you are using Bluebird for promises, there is a way to enable "long stack traces", but there is a warning not to do that in production, because it will put a strain on the server. Great. Then, later, async/await landed in Node, which exhibit the exact same broken stacktrace behavior, except, there is not a single thing you can do to get long stack traces out of those. Library authors have already started using this, so you might not even realize that you are affected by this, until an error pops up in production.

Implementing a "feature" like this into a programming language is batshit crazy in itself, but then it gets better, because a fix for this actually landed in Node at the end of 2018, but it's behind a --async-stack-traces feature flag. The broken behavior is the default, the fix is behind the feature flag, which is just turning up the crazy to 11. I understand that there might be performance penalties for enabling this feature, but then it shouldn't have made it into the language in the first place as a default.

Kubernetes, Docker

I moved all my personal stuff that I run into docker containers. I dreaded upgrading anything on the server, because something always broke. Last time it was a configuration setting change in emperor (python uwsgi stuff) that resulted in my blog being down. As a quick summary of pros and cons of putting things into containers:

  • Added security, every application is running in its own sandbox, so even if something gets hacked the blast zone is minimized, and listening ports are isolated unless explicitly mapped to the host.
  • Easier roll back, you can just run the previous image
  • Immutability, if the image ID matches another image ID, they are the same, there are no situation where you go "did I modify something manually without putting it back into git?"
  • Decoupling from everything else, if something requires some specific minor version of Python 2 to run, it's not a problem, it will not disturb anything else on the system, it will not disturb anything else on the system

Cons:

  • It is not always straightforward how to make dockerfiles for things
  • Everything is going to require more space
  • You need a docker registry to store images
  • Harder to debug things when they go wrong

Kubernetes is a container orchestration service, it's a thing that makes sure your containers are running, they get restarted when they crash, it provides discoverability for your containers (like how to connect your application to your database), load balancing, self healing, manages ingress routes, and a lot of others. Sometimes I see people stamping kubernetes as YAGNI, because "most people don't need the scaling it provides" but I think that's drawing the wrong conclusion, because it doesn't really depend on how scalable you need to be. Kubernetes gives you:

  • Infrastructure as code, as in you can put everything into version control, because all the configuration is declarative. It's easy to see "how did the infra look like 2 weeks ago, when problem X started?"
  • Resiliency, as in your applications will get restarted automatically when they crash, or enter into some weird broken state, without any intervention
  • Redundancy, so if one machine fails, kubernetes automatically redistributes the container to the other live machines, keeping the applications up

Which are all desirable at any stage and/or scale for a production application.

CSS Flexbox, and grid, CSS modules

This has to be the best thing that has happened to me in 2018. I built some real things with flexbox and grids, and they are such a godsend. Flexbox solves so many things that were major PITA previously, such as:

  • Floating things is extremely straightforward, and no longer requires clearing
  • Vertically aligning things is easy, even for variable heights
  • Reordering things for something like a mobile view is easy, and does not require markup changes
  • Same thing for stretching/shrinking content

CSS grids are also awesome, especially with the built in devtools in Firefox, that gives you a visualization of every grid. One thing that is easy to get hung up on is "should I use grid, or flexbox for this?", but this question becomes very easy to "intuitively" figure out after a while. A somewhat general answer would be, if you need to align things on one axis, you flexbox, otherwise use grids.

We started using CSS modules for a project, and initially, I was skeptical, but they turned out to be really great. I'm confident to say that the cascading part of CSS was a mistake, and it leads to more problems than it solves. With CSS modules I no longer have to thinkg about calling something "large" or "popup" or "notification" and worry about whether it's going to clash with something else. Since everything is sandboxed, I can have as many classes of the same name as I need to. This also makes it easy to detect unused CSS, since there is no cascading, you can safely delete the CSS when you delete the component that was using it.

Rust

This has to be my biggest surprise. I was always shying away from statically typed languages, because I did some Java very long ago, and the place I worked at had a gigantic, horrible codebase, where I had to wait 10-15 seconds after every change I made to see the result, and I attributed this slow feedback loop to Java, and statically typed languages in general. I gave rust a go, and I'm very amazed by it, the type system is really great at catching errors, if the code compiles it does what I expect. There is a major learning curve, at least for me, being used to dynamic languages, and the borrow checker still gives me a lot of problems where I jut scratch my head, but overall, I really enjoy playing with it. Hopefully I can land a job sometime where we use rust.