This post is part of the 2018 F# Advent Calendar. Check out all the other great posts there! And special thanks to Sergey Tihon for organizing this.
“Why F# is the best enterprise language” is not meant to be a clickbait title – it is my sincere opinion, and in this post I will attempt to justify it. If you stick around to the end, I hope that you will agree, or at least be a little bit persuaded. Read on!
Just to be clear, I’m only going to be talking about so-called “enterprise development”. I’m not claiming that F# is the best for systems programming, or games development, or hobby projects. What’s appropriate for one kind of development objective may well be inappropriate for another. Enterprise development has its own constraints and demands, which I think F# is particularly well suited for.
Nevertheless, having said that, I do think that the choice of programming language has an effect on productivity, maintainability, and stability, and that’s what I’m going to talk about in this post.
Of course, it’s easy to prove an assertion like “F# is the best enterprise language” – all I need to do is choose from one of the numerous longitudinal studies on enterprise software projects; or failing that, one of the many controlled experiments which involve large numbers of experienced developers.
So I don’t have any hard evidence, alas, but I will at least try to present a well reasoned argument! I’ll present my premises and then my conclusion. If you agree with my premises, I hope that you will at least take my conclusion seriously.
So what are some the key characteristics of “enterprise” development?
Software development is not the focus of the business
In an “enterprise”, software is generally treated as a tool; a cost center rather than a profit center. There is no pressure to have the latest technology, or to hire the best developers, or (sadly) to invest in training.
This means that being “enterprise” has nothing to do with the size of the business. By my definition, Google does not do enterprise development, while a 50-person B2B company probably does.
This also means that companies that develop in-house software to gain a competitive advantage, like FinTech companies, don’t count as “enterprise” either.
Projects are business-centric rather than technology-centric
The goal of enterprise development is generally to support business workflows rather than to implement a specific set of technical requirements. At the most basic level, typical enterprise software just moves data around and transforms it. This sounds trivial and is often looked down on as not “real programming”.
But business workflows involve humans, and any time humans are involved you will always have complexity. Implementing an efficient map/reduce algorithm or optimizing a graphics shader might be tricky, but possibly not as tricky as some business workflows! This 30-year old quote about COBOL sums it up well:
The bias against the problem domain is stated explicitly in [a] programming language textbook, which says that COBOL has “an orientation toward business data processing . . . in which the problems are . . . relatively simple algorithms coupled with high-volume input-output (e.g. computing the payroll for a large organization).”
Anyone who has written a serious payroll program would hardly characterize it as “relatively simple.” I believe that computer scientists have simply not been exposed to the complexity of many business data processing tasks. Computer scientists may also find it difficult to provide elegant theories for the annoying and pervasive complexities of many realistic data processing applications and therefore reject them.
Sadly, enterprise development has never been sexy.
Enterprise projects often have a long life
It’s not unique to enterprise development, of course, but it’s common that enterprise software projects live a long time (if they survive childhood). Many projects last five years or more – I am personally familiar with one that started in the 1970’s – and over the lifetime of a project, many developers will be involved. This has a couple of corollaries:
There is a very interesting talk by Robert Smallshire in which he simulates code generation for different size teams over different time periods. So, for example, after five years, the current team will generally only have contributed 37% of the code.
For a bigger team over a longer period, the contribution % can drop even lower.
Yes, these are simulations, but they ring true in my experience.
Enterprise project managers have a low tolerance for risk
As a result of all these factors, project managers tend to be risk averse and are rarely early adopters – why break something that’s already working?
As the saying goes “process is the scar tissue of organizations”. Stability is more important than efficiency.
However, new environmental conditions occasionally arise which force change on even the most conservative businesses. For example, the newfangled “intranet” and “internet” in the 1990’s scared a lot of people and had a lot to do with the rise of Java and VisualBasic/ActiveX. Here’s what the hype looked like back then:
Less than 10 years after those articles were published, the dominant enterprise programming languages had indeed changed to Java and C#.
Thanks to mobile apps and the rise of the cloud, I think we’re in the middle of another era like this, where enterprises are willing to risk new technologies so as not to get left behind. The challenge of course, is how to adopt new technologies without major disruption.
So how does all this affect choosing a programming language and its associated ecosystem, from a project manager’s point of view?
It should be enterprise-friendly
A project manager is not just choosing a programming language, they’re also committing to the ecosystem around the language, and the future support for that ecosystem. As noted above, enterprise development is not about being on the bleeding edge. Rather, if the ecosystem has support from an enterprise-friendly company like Microsoft, Oracle or Google, that is a big plus.
Also, from the enterprise manager’s point of view, it’s critical that the language and its ecosystem have deep support for enterprise databases (Oracle, Sql Server), enterprise web servers, enterprise authentication (AD, LDAP), enterprise data formats (XML) etc. It’s unlikely that support for the latest hotness will be their primary concern.
It should be future-proof
Given the longevity of enterprise projects, we want to make sure that the ecosystem and tooling will still be around and supported in, say, 10 years. If and when new platforms come along, you shouldn’t have to throw away all your code.
It should be flexible
And if you’re going to commit to an ecosystem, you’d ideally want to use it in as many different situations as possible (e.g. desktop apps, server apps, web apps) and different target platforms (Windows, Mac, Linux, mobile, etc).
It should make maintenance easy
Since the members of the team will probably rotate over the lifetime of the project, and most code will not be written by the current team, the dominant concerns are things like:
With these requirements in place, we can use them to reduce our language choices.
Here’s John Carmack on this topic:
The best of intentions really don’t matter. If something can syntactically be entered incorrectly, it eventually will be. And that’s one of the reasons why I’ve gotten very big on the static analysis, I would like to be able to enable even more restrictive subsets of languages and restrict programmers even more because we make mistakes constantly.
Software development is not the focus of the business implies that the emphasis is on stability and productivity, rather than, say, performance. This means that an enterprise programming language should not allow potentially dangerous actions such as control over memory and pointer arithmetic. Even if it can be done safely, as in Rust and modern C++, the effort to squeeze out the extra performance is generally not worth it. Letting the garbage collector take care of everything frees up time to focus on other things.
It should be enterprise-friendly so it’s no surprise that the favorites are:
So, far no surprises. We have come up with the usual suspects, Java and C#.
If this was 2008, we’d be done. But it isn’t, and we’re not. In the last decade, there has been an explosion of new languages which are strong contenders to be better enterprise languages than C# and Java. Let’s look at why.
Functional programming is the new hotness right now, but regardless of the hype, most modern programming languages are introducing FP-friendly features that make a big difference to software quality:
Resulttype for explicit error handling, and moving I/O and other sources of impurity to the edges of the application (as seen in the functional core/imperative shell and Onion Architecture approaches).
If we look at languages which support these features, we end up with the mainstream statically-typed FP languages (Haskell, F#, OCaml) and the more modern FP-influenced languages: Swift, Scala, Kotlin, Rust, TypeScript, etc.
As I said above, the rise of new technologies such as serverless means that enterprises will be willing to switch to these FP-influenced languages if they can provide a competitive advantage (which I think they do) and if the switch can be made with minimal disruption (which depends on the choice of language).
Some FP languages (Haskell and Scala in particular) support some features that allow high levels of abstraction. Some people like to quote Dijkstra here:
“The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise” – E.W. Dijkstra
That’s great, but I believe that in the specific context of enterprise development, too much abstraction can cause problems. If used too freely, it requires that all developers working on a project need to have the same understanding of the “new semantic level”, which is a burden on training and employability. All it takes is for one person to have too much fun with category theory in the code, and the code is rendered unmaintainable for everyone else.
That is, just as you can shoot yourself in the foot with low-level features, you can also shoot yourself in the foot with high-level features as well. For an enterprise language, we need to trim the top-end of the language capabilities as well as the bottom-end, and encourage an “only one way to do it” approach as much as possible.**
So I’m going to penalize Haskell and Scala at this point for being too easy to abuse.
** One of reasons people like Go or Elm as languages is because they are restrictive. There is a standard way of doing things, which in turn means that reading and maintaining someone else’s code is straightforward.
Are generics too advanced? 15 years ago, perhaps. But today it’s clear that it’s a mainstream feature. (The golang designers disagree!)
But how about lambdas? How about monads? I think that most FP concepts are on the verge of being mainstream now, and in ten years time will be commonly accepted, so it’s not unreasonable to have a language that supports them.
For me, in 2018, the “just-right” level of abstraction is that found in ML languages like OCaml and F#. In 10 years time things may be different, and we may be able to adjust the acceptable level of abstraction upwards.
However, I’m not convinced that more abstract, mathematical style programming (a la Idris, Coq) will ever be commonplace in the enterprise, due to the variation in employee skills. Yes, this could be solved with better training, or a certified-software-engineer qualification, but I’m not holding my breath.
If we then filter these newer languages by the “enterprise” criteria above we end up with the FP-influenced languages that support .NET and the JVM, namely:
To summarize the “why not language X” objections again:
Oh dear, none of the three finalists support them right now. I’ll let you judge whether this is a deal-breaker for enterprise development.
The three languages left (F#, Kotlin and TypeScript) are all good choices, they’re all open-source, cross platform, and enterprise friendly.
If you’re already using the JVM, then obviously Kotlin provides the best migration path. Similarly, if you’re using Node on the backend, then TypeScript is good (although trusting npm packages might be a problem).
But if you’re doing greenfield development (or if you are already on .NET) I believe that F# has the edge (and this is where I might be a bit biased!)
Of course, Kotlin can do some of these things and TypeScript some of the others, but I think that F# has the most breadth overall.
So there you go, that’s my conclusion! Feel free to disagree in the comments!
By the way, if you’re interesting in learning more about F#, check out the rest of the 2018 F# Advent Calendar, or if you like videos, here are some good ones that demonstrate its versatility:
|And if you are interested in the functional approach to domain modeling and design, here's my "Domain Modeling Made Functional" book! It's a beginner-friendly introduction that covers Domain Driven Design, modeling with types, and functional programming.|