Thoughts on Azure Functions and Serverless Computing

Monday, July 10, 2017

For the last 10 months I’ve been working with Azure Functions and mulling over the implications of computing without servers.

If Azure Functions were an entrée, I’d say the dish layers executable code over unspecified hardware in the cloud with a sprinkling of input and output bindings on top. However, Azure Functions are not an entrée, so it might be better to describe the capabilities without using culinary terms.

With Azure Functions, I can write a function definition in a variety of languages – C#, F#, JavaScript, Bash, Powershell, and more. There is no distinction between compiled languages, interpreted languages, and languages typically associated with a terminal window. I can then declaratively describe an input to my function. An input might be a timer (I want the function to execute every 2 hours), or an HTTP message (I want the function invoked when an HTTPS request arrives at functions-test.azurewebsites.net/api/foo), or when a new blob appears in a storage container, or a new message appears in a queue. There are other inputs, as well as output bindings to send results to various destinations. By using a hosting plan known as a consumption plan, I can tell Azure to execute my function with whatever resources are necessary to meet the demand on my function. It’s like saying “here is my code, don’t let me down”.

Sample Azure Function for HTTP Message Processing

The Good

Azure Functions are cheap. While most cloud services will send a monthly invoice based on how many resources you’ve provisioned, Azure Functions only bill you for the resources you actively consume. Cost is based on execution time and memory usage for each function invocation.

Azure Functions are simple for simple scenarios. If you have the need for a single webhook to take some data from an HTTP POST and place the data into a database, then with functions there is no need to create an entire project, or provision an app service plan. 

The amount of code you write in a function will probably be less than writing the same behavior outside of Azure Functions. There’s less code because the declarative input and output bindings can remove boilerplate code. For example, when using Azure storage, there is no need to write code to connect to an account and find a container. The function runtime will wire up the everything the function needs and pass more actionable objects as function parameters. The following is an example function.json file that defines the bindings for a function.

{
  "disabled": false,
  "bindings": [
    {
      "authLevel": "function",
      "name": "req",
      "type": "httpTrigger",
      "direction": "in"
    },
    {
      "name": "$return",
      "type": "http",
      "direction": "out"
    }
  ]
}

Azure Function are scalable. Of course, many resources in Azure are scalable, but other resources require configuration and care to behave well under load.

The Cons

One criticism of Azure functions, and PaaS solutions in general, is vendor lock-in. The languages and the runtimes for Azure Functions are not specialized, meaning the C# and .NET or JavaScript and NodeJS code you write will move to other environments. However, the execution environment for Azure Functions is specialized. The input and output bindings that remove the boilerplate code necessary for connecting to other resources is a feature only the function environment provides. Thus, it requires some work to move Azure Function code to another environment.

One of the biggest drawbacks to Azure functions, actually, is that deploying, authoring, testing, and executing a function has been difficult in any environment outside of Azure and the Azure portal, although this situation is improving (see The Future section below). There have been a few attempts at creating Visual Studio project templates and command line tools which have never progressed beyond a preview version. The experience for maintaining multiple functions in a larger scale project has been frustrating. One of the selling points of Azure Functions is how the technology is so simple to use, but you can cross a threshold where Azure Functions become too simple to use.

The Future

There have been a few announcements this year that have Azure Functions moving in the right direction. First, there is now an installer for the Azure Functions runtime. With the installer, you can setup an execution environment on a Windows server outside of Azure.

Azure Functions Runtime Installer

Secondly, in addition to script files, Azure Functions now supports a class library deployment. Class libraries are more familiar to most .NET developers compared to C# script files. Plus, class libraries are easier to author with Intellisense and Visual Studio, as well as being easier to build and unit test. 

Summary

Azure Functions are hitting two sweet spots by providing a simple approach to put code in the cloud with the scripting model, while class libraries support projects with larger ambitions.

However, the ultimate goal for many people coming to Azure Functions is the cost effectiveness and scalability of serverless computing. The serverless model is so appealing, I can imagine a future where instead of moving code into a serverless platform, more serverless platforms will appear and come to your code.

Adding Feedback from Solvingj:


Great summary! I always look forward to your point of view on new .NET technologies.

I wanted to add some recent updates you will surely want to be aware of and perhaps mention:

-Deployment slots (preview)
-Built-In Swagger Support (getting there)
-The option to use more traditional annotation-based mapping of API routes

-Durable functions in beta
This enables a stateful orchestrator pattern, which is always on, can call other functions, etc.  Whereas Azure Functions originally represented a paradigm shift away from monolithic ASP.NET applications with many methods to tiny stateless applications, durable functions represent a slight paradigm shift back the other way.  I think the end-result is Azure Functions supporting a much broader (yet still healthy) range of workloads that can be moved over from ASP.NET monoliths, while maintaining it's "serverless" advantages.

-Continuous Integration
I know you're aware of this feature since it's been part of Azure App service for ages.  However, you did not mention it, and for us, this was perhaps the most attractive feature.  The combination of Azure Functions Runtime + GIT integration enables the complete elimination of whole categories of tedious DevOps engineering concerns during rapid prototyping.  No Docker, no Appveyor, just GIT and an Azure Function App.  Of course, you can still add Appveyor for automated testing when it makes sense, but it's not required early on.

Other Experiences
-Precompiled Functions
I happen to agree with Chris regarding precompiled functions.  In fact, once we refactored away from the .csx scripts and into libraries, what we were left with was a normal ASP.net structure, with one project for the function.json files and all the advantages of the Azure Functions runtime (most mentioned here).   Once we switched to using Functions with this pattern, the entire Cons section you described no longer applied to us.

-Bindings
One of the Cons we found that you did not mention was in the binding and configurations strategy.  We liked the idea of declarative configurations and removal of boiler plate code for building external connections in theory.  However, in practice, the output bindings were simply too inflexible for our applications.  Our functions were more interactive, needing to make multiple connections to the outside within a single function, and the bindings did not provide our code a handle to it's connection managers.  Thus, we ended up having to build our own anyway, rendering the built-in output bindings pointless. I submitted feature requests for this however.

 


Comments
gravatar Miguel Castro Monday, July 10, 2017
Nicely said. Pretty much my opinion on them as well. I can't help but think of them as Microsoft's version of IFTT. I wanna get a chat bot taking to an azure function and back. It'll be my version of war games playing tic tac type toe with itself :)
gravatar Chris Surfleet Tuesday, July 11, 2017
Pretty much spot on. IMO its only worth going down the pre-compiled route, and there are a few gotchas with warmup times etc. But super powerful and a great way of only paying for what you use!
gravatar Frank Quednau Tuesday, July 11, 2017
Miguel, Microsoft's IFTTT is called Flow: https://flow.microsoft.com - an Azure function plays nicely as a component in such a flow.
gravatar Manuel Patrone Tuesday, July 18, 2017
Now that you switched you attention to the cloud, I wish you could dedicate some courses to Azure's competition. Case in point: the Faas arena. I've been looking at the Amazon offering and I'm impressed with the amount of innovation that I find in AWS. Lambad@Edge and Greengrass are examples. They enable IoT scenarios using Faas using architectures that I don't think are viable in any other cloud vendor (currently!). AWS Step Funstions are another example. On the other hand, MS and Azure have the best development story. Just a thought!
gravatar scott Tuesday, July 18, 2017
Yes, I agree.
gravatar graham Wednesday, July 19, 2017
one of the most interesting features is being able to see exactly where your money is going, something almost impossible using shared infrastructure: https://gojko.net/2016/08/27/serverless.html
gravatar Andrew de Rozario Saturday, July 22, 2017
Scott, I took your developing nodejs on azure course, created my own version of the function app and showed it to people at my work. A couple of non/ex-developers were really interested in the ease of accessibility. Do you think they could become really popular with BI professionals? There was also some interest in using them with Power apps and flow for adding a small amount of customization into that platform. Do you think there could be a substantial market for Azure functions in this area?
gravatar scott Monday, July 24, 2017
I think there is a possibility. Getting rid of all the overhead that comes with setting up an API makes functions approachable by people who just need to get something done.
gravatar Eugene Bekker Tuesday, July 25, 2017
A thought about your comment, "...the execution environment for Azure Functions is specialized..." -- with AWS Lambda, and their added support for C#/.NET Core 1.0 -- one of the features their SDK supports is something called "AspNetCoreServer" which allows you to write a conventional ASP.NET Core app and it will map the context to the Lambda infrastructure. It may not be perfect in all scenarios but I've been successful in some of the tests that I've run. (Here's a link to the feature: https://github.com/aws/aws-lambda-dotnet/tree/master/Libraries/src/Amazon.Lambda.AspNetCoreServer) Something similar could work in Azure Functions.
gravatar Daniel Mackay Monday, July 31, 2017
The biggest problem with Azure Function is their lack of binding redirect support for .NET. The problem is that if your function tries to consume an assembly with dependencies (which most enterprise app will have), and then the core of Azure Functions also has the same dependencies but at a different version, then your app will not work (e.g. log4net or newtonsoft.json). Currently there is no way to do assembly binding redirects like you normally would in your config file. There is an issue logged on github, but the fix is 6 months away: https://github.com/Azure/azure-webjobs-sdk-script/issues/992 Until this is fixed I don't see any way to use Azure Functions with .NET in an enterprise environment.
gravatar Marc Sunday, August 13, 2017
I've come to love Azure Functions and their AWS equivalent, Lambda. The biggest advantage I've seen is the speed at which you can deliver. I've seen it reduce time-to-production of new features by almost 20%.
Your Comment

My Pluralsight Courses

K.Scott Allen OdeToCode by K. Scott Allen
What JavaScript Developers Should Know About ECMAScript 2015
The Podcast!