I think of those as like different layers of abstraction. And we kind of have a fork in the road where we support two like fundamentally different styles of testing. Like the white-box versus black-box, I don't even know if that's the right way of describing it.
One is where you have no idea of the internals of the system. And that's our vision-based testing. And so for that, it's very much… we rely on visual cues. So you take screenshots of your application. And then we have a drag and drop workflow editor and annotation designer, where you take these screenshots, and then you annotate them and you say, you know, this logo placement and like this text on the screen probably means I'm on the home screen. And here's a bunch of other variations that might mean I'm on the home screen in this particular state.
You can use color, you can use recognition of an image, you can use motion on the screen. Or we have a bunch of different LLM integrations where you could say, “Hey, I'm on the screen, given the element of focus, navigate to this other place.” and it will generate the series of commands to navigate from A to B. All this is black-box vision-based testing.
The other route is I know the internals of the system, I'm using an Appium or a WebDriver under the hood, and it's more selector-based, HTML-based, DOM-based navigation. Where it's, you know, click on this particular element on the screen, wait for this event, wait for this element to pop up, assert this, assert that.
Those two paths allow different kind of capabilities. The BDD and all of those other things that you're talking about are all layered on top of the Appium-style testing. Whereas the vision-based testing, we can collect a competitive analysis report as one of our products. Where we actually take all the top apps in the app store, and we collect different metrics on them and publish. We collect hundreds of these reports on a ton of different devices, and then all the top apps… and I think we have like 12 metrics that we're recording right now. We run this report monthly, and this all uses our vision-based system because we don't have access to the internals of those apps to be able to do that style testing. So with that vision-based testing, we unlock things that you can't do with the Appium and other styles of testing.
In college, as part of a computer science curriculum, we took a programming language course, which got me into Haskell and functional programming. So, that was, like, my first taste of functional programming. Then I forgot about it because I got into Bloomberg. They, you know, indoctrinate you with C++ and Fortran. And actually JavaScript is a core part of the Bloomberg technology. They were doing server-side JavaScript before Node was even a thing.
There's some talks about that out there, so that's public knowledge.
I was working on Bloomberg's trading platform for the first half of my stint at Bloomberg. And then when I moved over to web, we were doing a bunch of cool stuff on Bloomberg.com. We completely re-platformed the whole thing. We were doing… we wrote our own single-page app framework – this is kind of like before React was popular, or really, it came out while we were building it – called Bloomberg Brisket, by the way, which is still a great name.
So, we were doing all of that, and, I mean, JavaScript was just fine, but I was not excited about it. I also felt that it was just fundamentally limiting in terms of performance with the whole event loop model, and wasn't particularly excited about the platform.
And so I got interested in two functional languages that were kind of getting a little bit of attention at the time. One was Elm, which I was playing around with quite a bit, which solved the front-end programming side of things. But on the back-end side of things, got really interested in Elixir, because it was my first introduction to pattern matching. Which is a language construct that is now actually in a lot of languages, but at the time was kind of coming onto the scene as the new hotness.
And then we had Bluebird, which was the first promise library.
Anyways, in Node, you can accidentally write synchronous code that's waiting for IO and the whole machine, like the whole process is just deadlocked, waiting for that. This can’t happen in Elixir, because processes, if they're doing IO, they just get preempted and go to the back of the queue. And so everything can be consuming resources concurrently and not negatively impacting each other. There's caveats to this, don't get me wrong, but that's the basis of the system.
It's also based on this concept of fault tolerance, where processes can be observed and linked together. So you can really gracefully handle failure. The ethos of the Beam world is let it crash.
So rather than worrying about handling every edge case, you just say, “Hey, let it crash. The system will reboot itself,” And it can reboot itself at arbitrary levels. So you just stick a supervisor, you put your processes under the supervision of the supervisor, and then if they fail, it will restart. Or you can even set rules, like if this process fails, also kill all of the sibling processes and restart them all together as a unit. So that fault tolerance is a huge piece.
You then get functional programming and message passing, which is really interesting and gives you nice isolation properties. But then what I think is also really unique about the Beam VM is its clustering and runtime capabilities. Where you can have many Erlang nodes that seamlessly mesh together, and then you can transparently communicate between these processes.
So I can have a process in EWR and LAX – if we're talking about airport codes – and I can communicate between them. And from the perspective of the programmer, they could be exactly on the same box. I communicate with them nearly exactly the same way.
So from all of these different underlying properties, you get a lot of really robust tools for building distributed systems in a highly available fault-tolerant way.
Well, a big piece of it is that the Websocket connections allow us to do real-time communication really easily. So for example, when a device connects up to our web platform and establishes that Websocket connection, we then use a feature of the web framework of Elixir Phoenix, and we use its presence features to announce “This device is connected and it is in an available state.” And then that information via CRDTs is broadcasted around the cluster so that no matter what device you're on, you can create the state of that device, show it in a UI or whatever, without having to like directly go to that device and communicate with it. Or to have a bunch of really complicated rules around device tracking.
So there's features like that, like Phoenix presence, that give us some kind of like unique, but like right out of the box features to do real-time state propagation.
Another is the broadcasting. So, you know, you love Redis, you know, you can have a Redis instance and you could do PubSub over Redis. But with Elixir, if you have your nodes clustered together, I can just do Phoenix PubSub broadcast, and then every UI that might care about some piece of information… Let's just say I'm building a dashboard for showing live sessions for my org. And I broadcast the message, “New session started.”, all the UIs could just be listening to that message, push the message update over Phoenix LiveView, which also uses Websockets, and boom, I have like a real live updating UI with like two lines of code.
So because we're doing so much real-time stuff, all of these capabilities that are really easy with the Beam VM because of the clustering capabilities and the message passing capabilities made it just like a really obvious fit.
The alternative, you know, I can do it in other languages and other platforms using Redis and message queues and things like that. We also still do use those technologies, don't get me wrong, but we get a lot of out-of-the-box features with Elixir by not adding any new dependencies.
We had this service at… gosh, I think it was... I can't remember if it was when I was at DealScience or Clicktrips because I've worked with like the same team everywhere. I just bring the same people out. I have engineers I like working with and I just bring them.
But a buddy of mine saw… like we rolled out… Oh, it was at ClickTrips. We rolled out this Elixir service and like the service had been taking like milliseconds before and like all of a sudden there was the little U. And I remember just being like, “What the fuck is this character?” Because all of a sudden it was just like insanely fast. But then when we're like looking at metrics and Datadog, there was like the little… I don't even know what the character's called. He's like, “What the fuck is… what does this mean?” I'm like, “It means it's extremely fast is what it means.”
I mean, it is very, very fast. We had that service… I wrote a blog post on this, “From $erverless to Elixir”. It was a system that we designed in Lambda and it was a great system. It was also a kind of a testing and uptime system. It was for a bunch of customers that we had in the travel space. And we were processing just tons of gigs a second of data. And we moved from Lambdas, which were costing us a fortune to Elixir.
The entire system, just like… I don't know, not 10X, the opposite of 10X, one-tenth? Like the response times were just insane once we cut over to Elixir. But then the other thing was like, we went from a system that was costing us $90,000 all in… I think in the article, I say like $30,000, but that's just our HTTP processing… to, I think we ran on about $150 of like spare Kubernetes compute. Like it was just running in like extra compute that we just didn't… it was just kind of sitting on those boxes.
And it took us a matter of days to like rewrite this service and like the cost savings was wild, but the user experience on the other side for people that were running this tool that we had, it was just like, “This thing is just lightning fast. Like, what'd you guys do?” And I was like, “Used a language that was designed for lightning fast.”