Simplifying web apps with Static Web Apps
In 2019, back when we attended events in person, Microsoft participated in Slush, a massive two-day startup event in Helsinki. We had a booth, and we wanted a way to connect with people and have them interact with our presence there without forcing them to have an uncomfortable sales conversation when really, they just wanted to see what we were on about.
We wanted to build an experience that was helpful, fun, and not at all salesy. There were a couple of ideas that we played with, but the one that stuck was Startup Adventurer. After all, building a startup is a bit like playing a classic adventure game: you start off with very little and learn and grow along the way – completing quests and earning experience. We wanted to allow entrepreneurs of all stages, of all stripes to see themselves as an adventurer, and to receive a little help along their journey from us.
Hello, Startup Adventurer
At the end of it all, we gave you an avatar you could download, as well as some content – curated based on the character you built – to help you on your way. We thought of it as our way of saying “It’s dangerous to go alone. Take this.”
How We Built It
We knew we wanted Startup Adventurer to work on a big touch screen (at Slush we used a Surface Hub 2S and some Surface Studios), but we also wanted to build it so that if we wanted to, we could pivot away from an in-person experience to something on the web. We had 6 weeks from getting approval to the event, so we need to move fast, with a team in Finland who were doing the artwork and front-end development and the back-end built by a team-member in Australia.
We decided to go with a React front end, that would run as a single page application in an Azure App Service and use Azure Functions as the backend, giving us a scalable serverless API platform that can store data in Azure CosmosDB with the built-in bindings.
To keep things simple, we went with a monorepo approach, combining the front and backend codebases into a single repo stored on GitHub. This would mean that everyone could work on the same codebase and run it end-to-end on their machines.
We wanted to have a Continuous Integration and Continuous Delivery pipeline setup, but since we would be deploying to two separate services in Azure, this was setup as two separate deployment pipelines, and depending on where the file changes in the commits were, the appropriate pipeline would be triggered. This reduced the number of deployments we were doing, but it ran the risk that our deployments would end up out of sync with each other.
Simplifying Our Stack
We needed to look at how we could simplify the stack so that we were able to iterate on the API and the UI as a single unit of work.
Since they were in the same repo, we decided on a process that saw the API mocked up, as in, we would have an Azure Function that would return a static response initially as JSON that the UI needed. This would mean that the UI could be built against a known backend, while the backend would be built on a separate branch. Doing the development this way would mean that the API work would not block the UI work.
But there were still two independent deployment pipelines which made us realise that we had more complexity in our stack than we ideally wanted since the APIs in Azure Functions were not intended to be used by anything other than our web application, but for all intent and purpose, they were separate applications to deploy.
We wanted to treat everything as a single application that we could deploy, and that we could deploy different branches with to allow testing without breaking the main release branch.
Breathing New Life with Azure Static Web Apps
With Slush behind us Startup Adventurer lay dormant until recently when we decided to look at Azure Static Web Apps (SWA) through a startup lens.
We decided to look at how we could take the existing Startup Adventurer project and migrate it to work on Static Web Apps.
Infrastructure simplicity was the first big drawcard of SWA, but the next advantage was around our development workflow. We wanted to overhaul the backend APIs previously built while doing some improvements to the UI, so we needed a way to deploy and test changes to the API without breaking the UI.
When a SWA repository receives a Pull Request (PR), it automatically creates a new staging environment and deploys the application to it. This environment is separate from production, meaning that we did not run the risk of breaking anything there, but it was still the same as production, allowing us to test with confidence.
SWA also gave us a bunch of features that we did not use this time, such as built-in identity management using social providers (Azure Active Directory, Twitter, GitHub) and in the Standard tier we can configure an external OAuth provider, such as Auth0. We get free SSL and custom domain support (both CNAME and APEX domain support) and monitoring.
This was a fun little journey for us, we took a project that could be considered a brownfields project and looked at how we could take what we learnt from the initial development to simplify how we’d do it next time.
With Static Web Apps we were able to drastically simplify our stack. It allowed us to treat our serverless backend and static frontend as a single deployment unit, keeping them in sync as we worked across the two teams.
By using GitHub Actions, we brought the CI/CD pipeline into the same platform where our code lived, no longer did we have multiple systems that needed to connect with each other.
The PR workflow that Static Web Apps supports meant that we stood up a staging environment to be able to test our changes directly in the cloud without having to manage additional resources, and having it automatically torn down when the PR merged avoids bill shock by accidentally leaving unused resources around.
There are other features of Static Web Apps that we didn’t use this time, such as custom domains and built-in OAuth support, which makes it easy to get an application up and running.