These are my notes from Reid Draper's presentation Production Haskell at YOW! 2015.
This talk was originally formerly Wrangling the Internet of things with Haskell, but changed in scope to simply Production Haskell.
In Reid's experience:
Of all of the projects I've worked on, our Haskell codebase has had the lowest defect rate with the same amount of effort of any project I've worked on.
It's also been the easiest to refactor of any language I've used.
OSS libraries for everything
$ stack build
Stack uses three layers of packagers, two of which are immutable:
This lets us spread our projects into many smaller projects efficiently.
Stack can use GHCi to very quickly/iteratively develop code, e.g. tweak a Web app, see the changes in the browser. See ordeal for more.
When we run
stack build, we get a self-contained executable called
app. This obviates the need for Haskell build infrastructure in the deployment environment.
Given a compiled project, which gives us a self-contained binary called
app, some assets, etc.:
All we need to do is create a tarball of the
app/ directory, name it with a short git SHA, and copy it to a deployment environment. Then we deploy it via:
$ tar -xzf app-370b347.tgz $ ln -s app-370b347 current $ restart app
The only mutation here is changing the
current symlink, so we can rollback in seconds by reverting the symlink. Deployment is fast. Redeployment is fast. Rollback is fast.
In production, under moderate load, we see usage of about 60MB of RAM.
An example property:
propEvent :: Event -> Bool propEvent a = Just a == decode (encode a)
The first time trying this, it failed due to a timestamp:
ghci> t 1894-02-22 00:00:04.5986 UTC ghci> Data.Aeson.encode t "\"1894-02-22T00:00:04.598Z\""
We lose a decimal place when encoding to JSON.
This is just some small subcomponent of our larger event; this is the kind of bug that, had we written manual tests for encoding and decoding this large thing, it's very unlikely that we would have come up with this example.
Even for relatively high-level tests we're able to find bugs that are really subtle in subcomponents in our data structures.
The problem that we want to solve is to be able to write reusable, composable queries that can be used within a transaction.
There's a short dive into postgresql-transactional here.