This is the second post in a series I’m writing about a company I’m starting up (or have started, depending on when you’re reading this). You can read other posts in the series here.
The BuzzwordsI’ve tried my hardest to make this post as approachable as I possibly can for anybody and everybody. I don’t want this to be something that is only interesting to folks who know what HTTP stands for or can rattle off it’s associated status codes. So for my non-tech readers, please bare with me for this one section and keep on reading.
For my fellow tech-nerds, I figured you might not care so much about the minute implementation details but rather are just more interested in a list of all of the pieces of our tech-stack (because you already know the implications of each in their use). So here’s the quick and dirty list of the main “techy” buzzwords that encompass our app:
- Rails (API only)
- JSON API spec
- Amazon Web Services (AWS)
- Route 53
- And of course, plenty of gems, third-party resources, plugins, etc.
Frontend vs. BackendFor our app, we decided to completely separate the architecture of the frontend (“frontend” being what a user actually views in their browser) and the backend (“backend” being all of the logic, data storage, etc. that’s taking place behind the scenes - not in the browser). The reason I specifically say “the architecture” of them is because although frontend is always separate from backend in how it’s viewed by a user, they’re not always built separately.
For example, if we had decided to build our application entirely in Rails (a more monolithic web-application framework), all of the architecture for both the frontend and the backend of the application would be written and maintained in the same place and each page on the site would be served up individually from the backend when a request is made from the browser (i.e. you refresh the page or navigate to a different page).
However, the approach that we’ve decided to take is to use a client-side (runs on the frontend) web-application framework called Ember to serve up a single-page application. This means that when you navigate to app.mailsnail.io, we serve up a page. Now, let’s say that within our app, you navigate to the Areas page (which I highly recommend you do because it’s awesome 😉), the URL would now be “app.mailsnail.io/areas”. With the more predominant web architecture (that most people are used to), the Areas page would have been served up completely separately.
What exactly does that mean? Well, you know when you sit and impatiently watch the page load icon spinning away in your browser? That means that your browser has sent a request to a server sitting somewhere out on the interwebs, asking it to give you that page. Each time a request is made (i.e. each and every time you navigate to a different page on the site), the server has to build and send back to you the entirety of that page. Even if there are only a small number of items on the page that are any different from the last page you were on, it still has to completely rebuild it from scratch.
How is a single-page application any different? I’m glad you asked! Using app.mailsnail.io as our example, when you navigate to that URL, our application will load in your browser and unless you specifically decide to refresh the page, which you shouldn’t ever have to do, no matter what you do in our app, no matter what pages you navigate to, we won’t ever have to reach out to a server to get you a new page. Say goodbye to frustrating page loading spinny icon thingy - it’s not welcome here!
API Only BackendSo how does this wizardry take place? Well, we do still actually have a backend and we are still dependent on servers sitting somewhere out on the interwebs. The difference is in how we access them. Traditionally, when a browser makes a request to the backend for a page, the backend builds out the entire contents of that page and then returns it to the browser. However, we’re only using our backend as an API (Application Programming Interface). In simple terms, an API is a way to provide some entity (in our case, a web browser) a means for communicating with our backend to request data.
Think of an API as a bouncer at a night-club. We don’t want just anybody getting access to our data. We only want users who we’ve verified as our own, who we trust, getting access to our data and really, even more specifically, we only want them getting access to their data. To do this, we first verify them via their username and password. Once they’ve been verified, utilizing an authorization standard called OAuth2, we issue what’s called an access token (essentially, a key - like with a house or a car) and store it in their browser. With each request to the API, we’ll send along this access token to make sure they are who they say they are. So the API looks at each request and determines whether it’s legitimate or whether it should be bounced harder than a bro with two overlapping popped-collar polos trying to get into The Roxy.
What this API enables us to do is that we don’t have to load an entire page every time a user moves around on our site. Instead, we’ll just request the very small amount (typically) of data we need and update the page the user is looking at in the browser accordingly. Generally, this creates a much more pleasant experience for a user because they don’t ever have to wait for page reloads and updates on the page are typically very fast because the data that we’re requesting is insignificantly small compared to an entire page.
We’ve built out our API using Rails and our API is made available for consumption on the internet via a fantastic service called Heroku. What Heroku enables us to do is not have to own and maintain our own servers to make our applications available on the internet. Instead, Heroku owns and maintains all of the servers. We simply deploy our application to their servers and they charge us for the usage. It’s the best thing since scented whiteboard markers (yes, I know the general idiom is about “sliced bread” but come on, how great is sliced bread really? I can slice my own bread but I’d be hard-pressed to make a whiteboard marker smell like cherries).
The Good, The Bad and The UglyAs with anything else, there are advantages and disadvantages to the approach that we’ve taken. Let’s look at some of those:
Code MaintenanceThis would definitely be in the “disadvantage” category. As I said before, if we’d built the entire application in Rails, we’d have only one codebase to maintain. However, since we’ve separated them into two completely separate entities, that means two completely separate codebases that we have to maintain.
And really, to make things even more complicated, there’s technically three completely different codebases that we have to maintain. One for our application, one for the backend API for the application, and one for our main landing/marketing page (www.mailsnail.io). I hear what you’re thinking — “but it’s the same domain (mailsnail.io), why would it be a separate codebase.” That’s a bigger question that I won’t go into here but just trust me on this one, there is a method to the madness.
Code DeploymentSeparate codebases doesn’t always have to mean separate deployment strategies but, in our case, it does. As I’d mentioned previously, our backend is deployed using a service called Heroku. However, our frontend (including our landing page) is deployed using a service called Amazon Web Services (AWS). AWS is similar to Heroku in that you can deploy applications to servers that they maintain, except they offer a whole lot more. We use three of the services that fall under the AWS umbrella but, last time I checked, there are approximately 53 gagillion services that they offer 😲.
The reason we deploy our frontend using a separate service is because at the time of launching our application, Heroku didn’t offer any stable, trusted options for deploying an Ember single-page application. I believe that this actually is something they offer now but I’m sure we’ll stick with AWS for our frontend for the foreseeable future because it’s working and AWS is dirt cheap — so c’est la vie.
What this translates to from a users standpoint is nearly instantaneous navigation between pages. And since we’re already requesting whatever new data we need while we’re changing out what you’re seeing in your browser, your overall experience in the application is smokin’ fast. Enjoy - it’s all for you! 😃
Third-Party Like It’s 1999As I mentioned in the “Buzzwords” section, we’ve used numerous plugins, gems, and other third-party resources in the building of our application.
This is nothing unusual in the brave new world of app development. Everything has become much more modular and there are services for just about anything you could possibly need. This helps a lot in getting an application from idea to launch a lot faster than it used to take because some of the more mundane or common tasks don’t necessarily have to take up your time and energy to build from scratch.
So it only seemed fitting to give a shout-out of sorts to some of the more important dependencies that have helped to make our application what it is:
- Lob - for printing and mailing services.
- Stripe - for billing and recurring payments for our subscriptions.
- StreetAddress - a Rails gem for address standardization and verification.
- Postmark - a service for sending transactional emails and notifications to users.
- ActiveRecord-Import - a Rails gem to drastically improve the efficiency of bulk data imports.
- JSONAPI-Resources - a Rails gem that helps to enable and enforce compliance for an API that adheres to the JSONAPI specifications.
- Sidekiq - a Rails gem that makes it simple and very configurable to run and prioritize background jobs.
- Mapbox - a map service that works with our mailing data to enable users to easily select mailing routes to target.
- Maponics - our data provider for the mailing data that we use in unison with Mapbox.
- Heroku and AWS - I’ve talked a bit about them previously but they both make it extremely easy to deploy an application into both Staging and Production environments, which is invaluable.
What I’ve LearnedTo be completely honest, I’m not sure I’d build an application again using Ember. I don’t necessarily regret our decision to use Ember for MailSnail. It’s a really neat frontend framework and it’s incredibly powerful. I’m very fortunate to have had the opportunity to learn the Ember framework (and still learning more every day), being that I had exactly zero prior experience in Ember before we started building the app. But the disadvantages I mentioned above can be quite cumbersome.
I feel like my co-founder and I have gotten well-versed enough in maintaining our client and API separately that it’s not necessarily a constant nuisance for us — we’ve simply acclimated to it as the way in which we build our app. But since the app I work on at my full-time job is built entirely in Rails, I’m reminded every day just how nice it is to be able to maintain and deploy your frontend and backend in one place.
That being said, I’m now able to claim experience in building Ember applications and building out Rails API-only applications, which is invaluable and I’m very thankful for it.
Overall, building the app, which will continue for the foreseeable future, has been a great but extremely challenging experience. It’s also been a very humbling experience. I’m a perfectionist at heart, so if left to my own devices, the app probably wouldn’t have launched for another year because I would have made sure everything was absolutely perfect (in my eyes). The problem is that you never really know what people want or will like until you simply get it in front of their faces.
With that in mind, one of my biggest takeaways from building this product is that you must get it in front of users as soon as you possibly can. It’s incredibly tempting to wait and wait. You can even convince yourself that if you launch too early, you’ll turn off users and you’ll never be able to recover. Set those fears aside. We launched our app in Beta in February of 2016 (roughly 6 months after we began building it) and I was more than a little bit embarrassed by the product. However, I’ll end with one of my favorite quotes, reminding me that we’re in good company:
If you are not embarrassed by the first version of your product, you’ve launched too late.
- Reid Hoffman, Founder of LinkedIn