Here are some examples where simply “choosing Haskell” will not automatically solve the concern for you at all:
- Laying down, communicating and embodying the right hierarchy of values in software development, especially in a group setting
- Choosing the right internal/intermediate data representations
- Imagining the right MVP functionality your software should have in a way that aligns with continued enhancement from there
- Knowing where making a huge deal out of every little issue prevents progress and where deep care needs to be taken even for the very MVP
- Choosing the right database — knowing when to use Postgres, Redis, DynamoDb, ElasticSearch, etc in concert as appropriate
- Knowing when a 300ms database call is just fine and when even 50ms isn’t acceptable.
- Knowing when no-redundancy is completely fine and when over-the-top redundancy is prudent
- Knowing when to use external async queues, their limitations, downsides, etc, and when to just get something done inline
- Figuring out the right database schema (remembering there’s always a schema even when using NoSQL)
- Enforcing the right amount of failure-tolerance on your functions, particularly those that do external calls, IO, etc.
- Striking the right balance in picking service boundaries
- Having good structured logging practices, and contra — knowing when too much logging itself hurts the system
- Having good instrumentation and performance tracking in your codebase
- Enforcing good clarity in the codebase — not too redundant but not prematurely abstract either
- Figuring out a way to achieve iterative growth on the solution in a team setting (usually easy for 1 person, hard even for 5)
- Striking the right balance in testing
- Knowing when to optimize coding style for long term correctness (i.e. type-tetris and all the noise from it) and when to optimize for ease of logic expression
The Haskell ecosystem has, perhaps, a larger than typical portion of its practitioners from the academic domain of interest. This is a particular strength of our community, but it often also means that more of our conversations are focused on topics like advanced type systems instead of, say, what a decent baseline application architecture should be for a variety of use cases. We have to keep reminding ourselves that the latter actually matters more for practical software — baseline Haskell2010 already gives you an outstanding toolkit but getting the above choices wrong can bust your project.
To be clear, commercial teams that choose Haskell usually do it because they believe Haskell will provide for a good syntax/language/coding environment that will bottoms-up bias the team in a positive direction. Yet we have all heard of battle stories where a given commercial effort’s failure was later blamed in part on the choice of underlying tech/language (e.g. Haskell). In my assessment, the root cause is often not the language, but bad choices made in systems architecture, development guidelines, team-wide values (I may say more on this at a later time) and other similar “macro” topics instead.
Haskell is a fine choice and many in our community really like it (myself included), but you still need to get everything else right. Abdicating your responsibility on good software governance because you “picked Haskell and so it’s all solved” will likely not work.
Many thanks to Ryan Trinkle, Doug Beardsley, David Drake and Michael Xavier for providing feedback on earlier drafts of this article.