Chasten - Rationale
Despite a recent surge in conceptual popularity, functional programming in the Node.js ecosystem remains fundamentally unsupported. Sure, there are some excellent utility libraries like ramda and sanctuary, but the fundamentals of serving HTTP requests are still left to mutation, statefulness, and other abject misery. What are those fundamentals, exactly?
- Taking a request and returning a response.
- Routing a request URL and method to a specific handler.
- Handling the effects produced by application code in a functional manner.
- Handling the co-effects of application code in a functional manner.
Some subset of these are sporadically supported by a few of the popular Node.js frameworks, but what if you want all of them? I couldn't find anything to help me out so I wrote Chasten.
What follows is a brief recap of why functional programming is preferable in general, and no attempt has been made to sugar-coat my strong opinions on the matter. If you find yourself disagreeing with much of the below, Chasten might not be a good match for you.
Prefer Functional Programming to OOP
Functional programming is a fundamentally good idea.
- Pure functions that take data and return data with referential transparency and no side effects are key to a maintainable program.
- Actions should be expressed by returning data that describe their intent rather than by performing them.
- Objects should be nothing but dictionaries of unchanging facts that pure functions can transform.
On the other hand, object-oriented programming is a major source of headache.
When application code is just pure functions and dumb data, the need to reason about how the world changed in response to previous function calls disappears entirely; the world didn't change. This is a huge relief for test code authors, who no longer have to be careful what they effect by using the functions of the domain they're testing.
When there is no state, the mental gymnastics required to understand a section of code tend to abate in difficulty. This also means there is no state to mock in testing, lessening the testing hassle.
Simple is not always Easy
Contrary to widespread belief, simple does not guarantee easy, and simple is more important than easy. If the two are in conflict, Chasten prefers to be simple by splitting interfaces into their constituent parts. When separate concerns are decomplected like that, reasoning about the system becomes easier, and assembling the system precisely as desired becomes possible. This is always desirable, even if it means applications now have to put more pieces together to work. Chasten goes a long way to keep you in control.
- Routing in web applications often conflates the concepts of finding the destination of a request, producing a response to a request, and even effecting the sending of a response over the wire. The three are entirely separate concerns and should not be mashed into one. You can handle requests without taking a stance on routing and, vice versa, you can route HTTP without worrying about how to handle a request if you want.
- Requests and responses are completely separate entities and should be kept separate. They're not the same object, it doesn't make sense to operate on both simultaneously, and neither has anything to do with sending data over the wire.
- It must be possible to separate the decision to run impure code from the implementation of how to do it. Otherwise you end up with muddy purity boundaries.
It should be possible to read the source code and correctly infer the behavior of the program. Surprises are usually bad surprises in the context of programming. "Magic", as some people take to call the weird things you never asked your program to do, but it did anyway, has no place in serious programming. A core tenet of a good code base is its readers' ability to know what it does - even after a year-long hiatus working on other code bases. These surprises tend to creep in with the best of intentions, and HTTP in particular has a great number of common practices and conventions that a web library tends to handle for you. Chastens chooses not to in the general case.