Seeing a SAFE app for the first time
I remember the first time I realised that it might really be possible to do a full-stack web app in F#. I was attending the DevOpenSpace conference for the first time, either October 2015 or 2016. I was pretty nervous as it was one of the first German-speaking conferences that I had attended and, as anyone in Germany who has known me for any length of time will know, my German sucked even more than it does today.
But Steffen Forkmann was there (who I already knew from F# and working together on FAKE and Paket), so I stuck to him like a limpet for most of the conference. We were in the hotel bar with some other people one evening and he whipped out a laptop and started demonstrating a combined Fable and Suave application, with hot module replacement in the browser doing client/server communication, all in F#, using Fable.React for the UI.
I could not believe what I was seeing. I had no idea had Fable really worked - I knew it was somehow a new version of FunScript but as my web knowledge was still stuck in ASP .NET WebForms days (with a tiny bit of MVC knowledge), I was sceptical that this could really be "a thing". Nonetheless, I felt that there was something there that would be worthwhile looking at.
Coming up with a name
Fast forward a few months and we at CIT had started to spend a little more time looking into the combined Fable / Suave combination, and we'd seen a few github repositories cropping up from people like Steffen illustrating how such a client / server model could work. My colleague at the time Anthony Brown and I were talking about ways that we could help provide a platform around which the community could coalesce and combine efforts for such a stack. We agreed to both try to come up with a brand name of some sort that fitted well. A couple of days later we hit upon the acronym SAFE.
Shortly after, we emailed a few close associates within the F# world to float the idea:
Just a quick email. We’ve (CIT) decided to start upskilling in Fable and looking at promoting a “standardised” F# + Cloud + Web stack that promotes functional-first patterns: Suave, Azure, Fable and Elmish, which we call SAFE:
• Use of the Suave Model: Promote the use of a functional-first server-side web pipeline, and could equally mean include Giraffe.
• Hosting web apps in Azure: Use of Azure for hosting web applications as opposed to self-hosting, which enables lower TCO. Depending on your preference, this could also mean AWS 😉 Essentially, it’s simply just “use platform services for web hosting”.
• Elmish for application model: Promote the idea of using the Elm model for simple, easy-to-reason about client-side applications.
This stack is nothing new in terms of technologies, but we feel that giving it a name and putting some material together on it could help the community in general around it. As you can see, some parts we want to keep deliberately flexible, but I think you get the gist here of where we’re aiming.
We launched a five-part blog series that culminated with the announcement of the SAFE Stack at Fable Conf 2017.
Creating the template
To start with, we had a Github organisation and a set of sample repositories. At around this time, .NET Templates were just becoming a "thing". Tomasz Heimowski had already started experimenting with a template for SAFE applications, so we immediately reached out and started working together to make it an official part of the project. The initial version was released in October 2017.
Moving to a template from a github repository was a big step forward - it enabled people to start working with SAFE without cloning a repository etc., plus it allowed some flexibility in configuring the base application.
We kept evolving the template incrementally - for a long time - and finally launched a v1 of the template in April 2019 - almost 18 months later.
Evolving the SAFE Stack
It's interesting to observe the changes that have been introduced since the original release, SAFE v1 and SAFE v3. Some changes were ones that we explicitly made; others were introduced simply to take advantage of new releases of components that the stack depends upon e.g. Fable or even language enhancements. Here's a few of them:
By the time SAFE v1 launched, we offered a wide variety of configuration options: for example whether you wanted to use Suave, Giraffe or Saturn; which Bulma template to start with; what NPM package manager you wanted to use etc.. Whilst this was useful and appreciated by users, we eventually realised that the cost of maintaining these configuration features within the template (and understand all the possible interactions between them) was simply too high.
We also had to maintain paket.lock files for each permutation of these configuration options - we ended up having to actually write a tool to automatically do this. By the time SAFE v2 and then v3 were released, we opted for a far simpler model: a template which had just two modes - "full" or "minimal" - which were highly optimised for two distinct personas. To replace the configuration options and features which were removed, we instead provided a wide variety of recipes that developers could refer to in order to see how to perform a specific task.
Taking advantage of .NET
One thing that SAFE Stack has benefitted from is aligning with standard .NET tools and libraries. For example, dotnet tools has meant that there is no requirement for users to manually install libraries such as paket and fake any longer; this is done automatically through
dotnet tool restore. Indeed, whereas SAFE v1 began with a FAKE script we nowadays use an actual .NET application to launch SAFE apps, meaning you can start your SAFE app with just
We also moved from Suave to Giraffe as the "foundational" F# web library. There were several reasons for this, one of which was to "ride the wave" of ASP .NET Core; as improvements are introduced to the core platform, Giraffe can take advantage of them - which cascades up to SAFE Stack users.
Another benefit is the massively simplified set of NuGet dependencies - the initial versions of the stack had a Paket lock file that was ~1500 lines long. SAFE v3's template is just 220 lines (in a single Paket group). Part of this is the bundling of ASP .NET Core's dependency chain directly into .NET, as well as the general simplfication of the way that core .NET libraries are resolved as NuGet packages between .NET Core 2 to .NET 5.
F# language improvements have also found their way into everyday use in the stack. Some of those include making the
yield keyword optional, which has a significant improvement on simplicity of Elmish views. Other syntactic improvements include string interpolation, which have improved readability of code, especially for beginners.
Core stack enhancements
The core components in the SAFE Stack have gradually evolved over time. As already mentioned, we transitioned from Suave to the combination of Giraffe and Saturn relatively early in the lifetime of the stack, but that's not the only change that was made. Fable has evolved and nowadays doesn't require Babel any longer; this change has not only improved performance and reduced complexity of using it (it's now just a standard dotnet tool) but has opened new avenues for use.
We've also introduced the use of Fable.Remoting as the default communication layer between client and server, and moved to using Feliz as the default UI component family and style.
Another ecosystem benefit has been to utilise Farmer and the Azure CLI for deployment to Azure, removing what was previously a much more complex process especially when it came to authentication. Now it's possible to create a new SAFE app and deploy to Azure simply with
dotnet run azure.
I think that SAFE is a good example of an F#-first stack that sits on top of .NET, making use of common tools and features of the platform, without compromising on its original goal, which was to provide a compelling web development experience that treats F# as a first-class citizen across the whole stack.
We've certainly come a long way in the last four years. New people have stepped up to contribute to the ecosystem, libraries have come and gone - but the original aims of the stack remain in an even more streamlined fashion, with excellent documentation that continues to grow.
I'm looking forward to the community continuing this trend, whilst growing the community - and making F# web development the best way to write functional-first web applications anywhere.
Have (fun _ -> ()) with SAFE Stack!