BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Presentations Full Stack Dart

Full Stack Dart

Bookmarks
38:53

Summary

Chris Swan discusses using a stack of Dart, where Flutter developers can use the same language to build the services behind their apps.

Bio

Chris Swan Chris Swan is an Engineer at The @ Company, building the @platform. He was previously a Fellow at DXC Technology where he held various CTO roles. Before that he held CTO and Director of R&D roles at Cohesive Networks, UBS, Capital SCF and Credit Suisse. Chris co-hosts the Tech Debt Burndown Podcast and is a Cloud Editor at InfoQ.

About the conference

QCon Plus is a virtual conference for senior software engineers and architects that covers the trends, best practices, and solutions leveraged by the world's most innovative software organizations.

Transcript

Swan: I'm Chris Swan. I'm an engineer at The @ Company, where we're building a platform that puts people in control of their data. We're using Dart to do that. I'm also a Cloud Editor at InfoQ. I co-host the Tech Debt Burndown Podcast, and I'm a blogger at thestateofme.com. You can find links to socials, at chris.swanz.net.

Outline

I'll start off talking about why Dart. Where the language came from, and why it's been recently surging in popularity. I'll then explore a few of the language features, what makes Dart different from some of the other languages out there. In particular, I'm going to look closely at the differences between Just-in-Time compilation and Ahead-of-Time compilation, and take a look at Dart on Docker, as a way of deploying Dart into server side environments. There's some great debugging, profiling, and performance management tools built into Dart. I'll finish with a call to action with some easy ways that you can try out Dart and Flutter.

Why Dart?

Why Dart? Dart emerged from Google a little after Go, and was aimed at the client side development. They described Dart as a client optimized language for fast apps on any platform. You can find out more about it at dart.dev. Originally, there was an idea that Dart VM would go into Chrome, and people would be able to write faster webpages using Dart as a VM based language, a bit like Java with applets in the early days of the web, but that's not what came to pass. What's happened instead is Flutter. Flutter is described as Google's UI toolkit for building beautiful, natively compiled applications for mobile, web, desktop, and embedded devices from a single code base. You can find out more about Flutter at flutter.dev. Flutter has surged in popularity recently, firstly as a framework where people could write applications that run across iOS and Android, and more recently as a framework that people can use to build web and desktop applications. Maybe even apps that cross over mobile, web, and desktop from a single code base. Flutter has really pulled Dart up into the RedMonk rankings, and it's now in the top 20. The most recent RedMonk research on language popularity, which they base upon data that they get out of Stack Overflow, and data that they get out of GitHub repos, found Dart being in the top 20 position, edging out Perl which had previously been there.

Language Features

Let's take a look at the language features of Dart. Hello World is going to look similar to many other languages that people will be familiar with, such as Java, C#, or really anything that's a C derived language syntax. We've got a void main function here, and that's simply invoking print for Hello World. Being a virtual machine language, similar to Java and C#, we can make some of the same garbage collection mistakes as we can with Java and C#. Here's an example of an antipattern where we're creating some arguments, and then parsing those arguments into a value object, and then doing some null checking on that object and returning it. This creates lots of temporary objects, lots of garbage, and gives the garbage collector lots of work to do later on.

Dart has an async/await construct a bit like JavaScript. In this example, the main section of code is calling the create order message function that in turn is awaiting an asynchronous invocation of the fetch user order function. Inside of that fetch user order function, there's a 2 second delay before it's returning a Large Latte. In this case, the code is firing off asynchronous function invocations and then awaiting the return of those, and it gives a really tidy way of programming asynchronously. For concurrency, Dart has an isolates construct. If you've not already come across isolates, they're described as being independent workers that are similar to threads but don't share memory, communicating only through messages. This will be very familiar to people that have come across languages such as Erlang. With Dart, it's pretty straightforward to create an actor model in the language because of this isolate based concurrency.

A recent addition to Dart is sound null safety. That arrived in March this year, and prevents variables from being nulls. In these top examples, a number of different variables are being defined. We can see an example of duct taping where there's an inference of the variable i being set to 42, becoming an integer. In each of these cases, those variables can't be set to null. That would create a compiler error at compile time. In order to have backwards compatibility, Dart still has nullable variables, and they're indicated by adding a question mark to the end of the type declaration. In this case, we can define an int question mark, a nullable int, and set that to null.

Dart and most of the Dart ecosystem around the core language are licensed using the BSD 3-Clause License, and that's an OSI approved license. This is one of the things that I think gives the community some confidence, that it's not just a Google thing, and that there's a healthy community building up around the language. That the language would be able to have a future, even if for whatever reasons Google chose to deemphasize its own use of Dart and Flutter. Though, I'd also point out that Google are using Dart and Flutter for a great many of their own things right now, which is helping the language and its ecosystem to become more vibrant.

The package manager for Dart is called pub.dev. That can be found at pub.dev. Within an application, a set of dependencies are defined using a YAML file called pubspec. There's quite a sophisticated solver on the backend, which will take the declarations from that pubspec and find the point versions that will match the demands that the application is asking for. When it comes to actually building apps, then dart pub get or flutter pub get are used to fetch the dependencies from pub.dev.

JIT vs. AOT

Taking a look at the various performance tradeoffs between Just-in-Time compilation and Ahead-of-Time compilation, this is one of the areas where Dart is a little different from most other languages. Because in most cases, languages support either Just-in-Time compilation or Ahead-of-Time compilation, but Dart offers both. To illustrate what's going on here, I'm going to use a trivial application. This is really just a more useful version of Hello World. In this case, it's printing out platform version information, which gives an output very similar to what we'd get from running dart --version. That's done by importing the platform function, and then invoking that with version.

For Just-in-Time compilation, I can just dart run the source code in the virtual machine. If I time that, then I can see that it takes a little over 6 seconds to get an output. In this case, it's showing me that I'm using the stable version 2.14.4 of the SDK, and that I'm running it on a Linux box on Arm64 hardware. That's quite a bit of time. That's because time is being spent spinning up the virtual machine, parsing the source code into it, turning that source code into bytecode, and then running that bytecode.

It's a different story with Ahead-of-Time compilation. Here we're taking the source code and compiling it into a platform native binary. I'm taking the time that it takes to run that platform native binary, which is only a bit less than 0.03 of a second, so, much faster than spinning up the same code in a VM. Of course, there's a tradeoff here. The compilation process to produce that native binary is longer than running the source code through the virtual machine on its own. In fact, there's a tradeoff in many different dimensions here, because although the Ahead-of-Time compiled code is fast, and has no cold start overhead, making it very suitable for Functions as a Service types of environments. It doesn't benefit from the Just-in-Time compiler, being able to see the actual running code in its running environment, and thus able to make optimizations over time. Some of the things that we've become used to in terms of the long running performance optimization of Java SE, those can be done with Dart running in a VM. That's traded off against the overhead of starting up the VM.

Dart on Docker

Looking briefly at Dart on Docker, this is the way that most Dart code is going to get deployed onto the server side. Because Docker containers provide a really convenient way of packaging up the Dart code, whether it's been turned into a native binary, or whether we're running it inside of the VM. Here's a typical Docker file for an Ahead-of-Time compiled dart binary. It's a two-stage Docker build. In the first stage, we're taking some source code and turning it into the binary. In the second stage, we're taking that binary and adding it to a minimal layer of libraries and defining an entry point. Going through line by line, FROM dart AS build, is making use of the official Dart image, which packages up the SDK and all the tools that are needed to build Dart applications. We're defining a working directory and copying some source code into that working directory. Dart pub get then goes off and gets any dependencies from pub.dev. Then dart compile exe is taking the source code and turning that into a native binary.

In the second stage of the build, we're starting from scratch. This will be a pattern that will be particularly familiar to the Go community where it's fairly typical to deploy a statically linked Go binary into a scratch container. Dart, though, can't have statically linked binaries. One of the design philosophy decisions early on in the language's development is to have it dynamically linked, and so we need a layer of runtime libraries in order to sustain that dynamic linking. They can be taken from the runtime directory inside of the official image. They're pretty small, only a couple of Meg, so it doesn't blow out the resulting container. We then copy in the binary that's going to make use of those libraries, and the entry point is defined at the end.

Looking at the image sizes built from that Docker file for a trivial application like dartshowplatform, then we can see that the images are only 3 to a little over 4 Megs, dependent upon the underlying platform. 32-bit Arm results in the smallest images, up to 64-bit x86 with the heftier images, but not a huge amount of difference. It's still a pretty good story for a non-trivial application. These are the images for the secondary server in our privacy preserving platform. This is a private datastore where people can store their data and give fine-grained control over who has access to it. The Docker image for that is still beneath 6 Meg on x86, 64, and is a little bit smaller for the Arm variants. That gives us a variety of different places that we can run it. It's possible to have a very lightweight, quick starting container image containing Dart code.

Debugging, Profiling, and Performance Management

Turning now to debugging, profiling, and performance management. Dart and Flutter come with a comprehensive suite of developer tools known as DevTools. This matrix illustrates which of the DevTools are available for which of the target environments. Web is a little bit lacking in tooling, because the tooling is instead really in the browser for that. For Flutter mobile and desktop applications, there's a full suite of tooling, with debugger, logging view, upsize, CPU profiling, memory and network views, performance, and the Flutter Inspector. Pretty much all of that is also available for command line applications that we might run server side. The only missing piece there is Flutter Inspector, which of course is dedicated to Flutter apps.

Having a look at some of the prettier charts that can be generated from DevTools, the memory view is excellent for figuring out how often an application is growing its memory utilization, where garbage collection is taking place, and how snapshots are happening with that. Flame charts for CPU utilization are really helpful for finding hotspots in code and being able to deal with those. I would note, however, that DevTools needs profiling support, and so that profiling is built into the virtual machine, so on the server side, if running in Just-in-Time on the virtual machine, all of that is there. Whereas on server side with an Ahead-of-Time compile binary, the profiling isn't there, unlike the snapshots that are provided for the various mobile application and desktop application environments, which can still provide profiling information.

Call to Action - Easy ways to Try Dart and Flutter

Coming to a call for action, here are some easy ways that you can try out Dart and Flutter. Dart has an online application called Dartpad that allows Dart applications to be run in the browser. In this particular case, a Google Codelab is illustrating the building of a Flutter application. On the left-hand side is the source code in the Dartpad environment, so it can be run live. On the right-hand side is a form that's been built using the Flutter framework. There's a link, https://flutter.dev/docs/get-started/codelab-web that will take you to that code pad example, where you can try that out.

For server side, I'd really recommend giving Qwiklabs a try. Particularly, this lab dedicated to the Dart functions framework, which runs through building a full stack application, so Flutter at the frontend, through to some Dart functions at the backend, running in Cloud Run on Google Cloud Platform. With Qwiklabs, that will give not just the training setup, but also a burner GCP account in order to run that lab and try it out. Google almost continuously have training promotions going on related to Qwiklabs that allow free 30-day subscriptions to be set up so that you can try out this Qwiklabs and some of the other ones.

Review

I started off talking about, why Dart? What's different about Dart from other languages? Why is it surging in popularity? That mainly comes down to Flutter and the adoption of that as a cross platform frontend development framework. I've looked at some of the language features of Dart, which make it distinctive from the other languages out there. Particularly, the ability to have both Just-in-Time and Ahead-of-Time compilation from the same code base. For doing full stack Dart, it's really handy to be able to deploy Dart on Docker, and having official image support for those two-stage builds is very helpful. Also, very helpful are DevTools, which provide really rich views onto debugging, profiling, and performance management. Getting started with Dart and Flutter is very easy. You can do it with just a browser. My call to action would be, if you're not familiar with Dart, give it a try. It's very easy.

Questions and Answers

Schuster: Dart versus Go, what are the typical use cases for Dart and for Go?

Swan: I think it's, first of all, interesting that both of these languages have emerged out of Google, and that Google's developing stuff internally using them both. If I look at what Google are actually doing with that, then they're using Go for developing infrastructure software, and Kubernetes would be a perfect example. Really, it's a redo of Google's Borg in a manner that they've been able to open source and turn into a very successful community project, and then built a whole bunch of services around it. I really feel like Google's trying to make Google Cloud the go-to place for running Kubernetes. Of course, they've got competition from AWS and from Azure and others on that. Whereas Dart and Flutter are being used to develop frontends for things. Flutter in particular, is giving the ability to develop the same application that's going to run on, firstly, Android and iOS, but then later Android, iOS, and web. Then now, Android, iOS, and desktop. One of the things that caught my attention on that was Canonical choosing Flutter as the new desktop UI framework that they're using for the Ubuntu installer. I think that's a place that many people will be having an encounter with a Flutter desktop application, and probably not even realizing it, because we often don't ask which frameworks have been used to develop desktop apps.

I think there's people asking for OpenTelemetry, and it looks like not very much progress is being made on that.

Schuster: Is Dart mostly applicable for mobile dev? How about creating microservices, or serverless? I think you brought up Qwiklabs.

Swan: Yes, the whole point of the conversation of full stack Dart is if Dart adoption has been driven by people using Flutter to develop mobile apps, web apps, desktop apps, then in most cases, those apps are going to have some backend. Are you going to go and learn a different programming language to write that backend or are you going to use Dart to write the backend? Dart's always had the ability to write command line applications, and so if you can structure your backend as basically just a command line application, then that's immediately going to be a good fit. Things like the functions framework, are then providing easy ways to write serverless functions in Dart. There's not yet any Functions as a Service platform that's got native Dart supporting it. If we look at the direction Google's been taking with this, then they've got Cloud Run, which is basically serverless platform running containers. They're just making it easy for you to put Dart into a container, and then you can throw that container onto Cloud Run or anywhere else that you can run containers in an event-driven environment.

Another piece to that as well has been recently the multi-platform support for Dart on Docker. Recently, I was still maintaining a fork of the official Dart image for Docker, in order to have Armv7 and Arm64 support. That's now being put into the official image. You can do FROM dart and expect that to work on x86, Arm64, Armv7, which obviously means that we can keep the Raspberry Pi's and the AWS Gravitons happy with all of that stuff.

Schuster: That's what we all want to do, of course. I seem to recall that Google App Engine used to have Dart support. Is that not here anymore? In the old days, like five, six years ago?

Swan: App Engine is still talked about as a target for Dart from a full stack Dart perspective, but that's using App Engine to run containers rather than using App Engine with native language support. Way back in the early days, there was even talk about putting a Dart VM into Chrome. The reality is, things went in a different direction. What was previously dart2native, which became dart compile is all about taking Dart and turning it into platform native binaries, whether that's in iOS or whatever else.

Schuster: How does Dart compare to Kotlin.

Swan: I've not had direct experience with Kotlin. That's one I'm going to struggle with. The nearest comparison I could make is with Swift. If we think about, essentially, Kotlin and Swift has come along as the next revs for native development on iOS and Android respectively. Swift is catching up on a couple of decades of language development, in order to get us out of doing things with Objective-C, and into doing things with a more modern approach. My very limited understanding of Kotlin is that it's essentially trying to do the same thing for Android, so move on from Java. I expect that some of the motivation for Kotlin may also have come from the difficulties that Google was having in the lawsuit with Oracle over the Java APIs. I think I saw as well as a hedge against losing badly in that arena. Thankfully, that didn't come to pass. I think Java now has actually quite a pluralistic future. We've seen Microsoft step up into the Java Community Process. I was talking to some Microsoft people, and it's kind of, who would have ever expected that outcome back in the days of Visual J++? Strange things happen over time.

In terms of the modernity of the language, there's lots of things in Dart which have become a pick and mix from good things that have been going on in the general language landscape. Things like Unicode support are really good. I think, in that case, it's clearly a modern language that's in very active development, and keeping up with what people want and what's becoming possible. Also, it's a language that's not embroiled in some of the issues that we see with Go at the moment over, are we going to have generics or are we not going to have generics? Those things are already settled. I can't make a more precise comparison to Kotlin. It feels modern in the same way that Swift feels modern in that there's definitely a couple of decades of stuff that there is language features from birth or that have been added in elegantly rather than other languages feel like they've had stuff bolted on, but it doesn't fit properly because it was never conceived when the language first came out the door.

Schuster: Dart was designed with integers, which is still science fiction for JavaScript. They just added big integers, but only 30 years after the conception of JavaScript.

Is Dart still optionally typed? Types are still optional.

Swan: It's really a strongly typed language. In terms of declaring variables, it will do duct taping. I think there was an example in one of my slides, illustrating null safety. You can basically say, variable equals integer, and it'll go, "That looks like an integer to me." Using duct taping, that's an integer. Within the compilation and then subsequent bytecode interpretation on native compilation process, that's going to be treated as an int. It's not like it's later on fungible to go, "Yes, it looked like an int initially but now I'm going to throw a string in there," that will definitely give you a type error.

Schuster: It's interesting because, I think when it started, it was pitched as a cleaned up Java, minus the static typing, so you have the flexibility of you type when you need to, when you need the documentation, but you can be as flexible as JavaScript, you just type your code and deal with the dynamic consequences, in a way.

Swan: I think we're often seeing industry oscillations between, do people want strongly typed or do people want duct taping, or no typing? I think what we've seen in the adoption of TypeScript is, people want to access the JavaScript ecosystem but they want the additional safety that they get from having a strongly typed set of tools around the language. I think for similar reasons, actually, Dart has become more typed as time has gone by, and the null safety stuff that came along in March, which is very rapidly actually propagated through the pub.dev package ecosystem. Just about all of the packages that are in typical use are now updated to support null safety, is essentially putting stronger typing over an already strongly typed approach in the language. Yes, I think there's some very limited duct taping going on there as just a readability and programming convenience, but treat it as a strongly typed language in terms of how it's working under the hood.

Schuster: What's the value type situation? Can I define my own value types that can go or is it just primitive, like numerical types that are value types?

Swan: I don't know that I can actually answer that, because it's not something that's come up in my day-to-day interactions with the language. I just don't know.

Schuster: Ahead-of-Time compilation is, I think, one of the exciting things just so you don't pay for the startup time all the time. What other things would you say are advantages over something like TypeScript? Because if I look at TypeScript, it seems it's a larger code base or larger user base, looks like a little bit if you squint. How do you sell Dart over TypeScript?

Swan: Where I see this going is actually, I think, even more exciting, because what if you could Ahead-of-Time compile to basically give the VM a really fast startup, but then slide in actually using the VM and JIT so that you got long-term optimization from how you're actually using the application and what's going on there? If you look at, again, a little bit under the hood, what the compiler is doing there is generating things that they call snapshots. When we do an AOT binary, we're basically taking a snapshot and we're putting it in a very thin wrapper, so that it'll run on a given platform. One of the possibilities there is we could take a snapshot and actually pour it into the VM along with the source code or the bytecode that we're generating from the source code, and get a best of both worlds between AOT and just running in JIT in the VM. I think that's something distinctive that Dart has the promise to accomplish, which would stand it apart from most of the other languages that we're used to, where you're making a language level choice of whether you're interpreted, or JITed, or compiled.

Then I think this also relates to what's going on with WebAssembly, because a lot of the time, if you're hitting limitations with JavaScript or Typescript and saying, what would I rather do? The answer might be, add a binary Wasm module in instead to solve that problem, just be optimized for image manipulation, or whatever the specific thing is. I think Dart is also going to be able to offer the best of every world there in that you've got AOT and JIT, and maybe AOT and JIT together, but also the ability to be a Wasm module, and to incorporate Wasm modules.

Schuster: The AOT stuff is really exciting. What's the Wasm story for Dart? How is it today?

Swan: There's two still experimental things going on with Wasm. One is the ability to use Wasm modules. The other is the ability to create Wasm modules or compile into Wasm modules. I think the team at Google are looking at those and basically seeing how useful they become and how much support people need for those. Definitely the ability to use Wasm modules really blows wide open the possibilities of, if I can't get that in the language and the framework and the pub.dev modules from what's just there for taking, then actually I can open up that from other languages and get things from Go, or Rust, or C++, or COBOL, or wherever else I might find it. Whereas making Wasm modules then opens up the possibility of, can I run my stuff in Cloudflare Workers as opposed to some of the other container based platforms that I might be looking at?

Schuster: Have you looked at Google's Fuchsia project in any way? I think Flutter is quite important for that.

Swan: Yes. I think myself and many of my colleagues are keeping half an eye on what's happening with Fuchsia, and really the opportunity there to have a clean slate OS. It's obviously got potential for handsets. It's also got potential for IoT, which is something that we're quite focused on. I think the nice thing from our perspective there is, if we're doing stuff with Dart and Flutter, and Fuchsia is relying on Flutter as one of the main ways into it, we'll probably be able to use Fuchsia without having to change anything.

 

See more presentations with transcripts

 

Recorded at:

May 27, 2022

BT