We’re celebrating today because we just launched the new EricElliottJS.com — a site packed with learning content for app developers. Within seconds of sending announcements to users of the old site, user registrations came flooding in. We can’t wait to hear what everybody thinks of the new design, content, and app UX, but in the meantime, I can’t help but reflect on all the lessons we learned in the process of building it.
Here are some of the features we built for the new site:
- Passwordless sign-in with GitHub authentication via Firebase
- Lesson player with markdown support, syntax highlighting, and progress bar
- Interactive code editor which runs unit tests
- Email support form
- Video embed
Before we dive in, a quick tour of the tech stack. We used:
- React for UI
- Redux for predictable state
- Firebase for a live, self-scaling db connection
- Mailgun for email sending
- Unit testing with RITEway
- Functional testing with TestCafe
Lesson One: Passwordless Authentication Rocks
Every time you create an app which needs to store usernames and passwords on behalf of your users, you create more surface area for hackers to attack your users. Passwords provide notoriously bad user security, and they’re not particularly good for the user experience, either. Users have to remember yet another password, fiddle with password managers, or (much worse) reuse a password from other sites.
Many users reuse the same passwords for almost every site, which makes both them and you vulnerable to hackers who have stolen databases from elsewhere. Even if your own password security is amazing (unless you have a big security budget, trust me, it isn’t), your security is still wide open to hackers who already have working usernames and passwords from other sites who weren’t as careful.
We’re targeting developers, so we opted to use sign in with GitHub, making sign in for most users one-click experience. So nice!
Lesson Two: Redux is Still Cool
Our user sign-in status is shared between several otherwise independent components, including:
- The header, where we display a user avatar to let users know they’re signed in.
- The hamburger menu drawer. The header menu collapses on mobile devices, but users can still access the links in the sidebar. It needs to know if the user is signed in so it can know whether to display the “Sign In” link or the “Sign Out” link.
In our first pass, we were using a custom Next.js
_app.js file to manage our global state, passing it through to the components, but that was more work than simply wiring up Redux. Redux lets you use
react-redux to wrap around a unit-testable display component and create a container component using
connect(). Now, when we need to grab some state that’s shared between several disconnected components, that’s a breeze.
This isn’t our first trip around the React+Redux block, so some of us knew this already, but:
- We had a couple junior developers on the project without as much experience with React+Redux, and
- React hooks were released during the course of our site build, so we did do some rethinking about when it’s appropriate to do things in different ways.
Even with React hooks on the scene, we’re still fans of the ease of use of
Lesson Three: Before You Launch, Make Sure You Can Scale
Very soon after announcing the new site, we exceeded the quotas of the free Firebase plan. I assumed it was just going to bill us based on usage, and did not realize we had it set to the free tier. Oops! Make sure you have the service capacity required to serve your users.
I wouldn’t stress out about this too much if you’re starting a new app, but we already had a database full of users and we emailed them all in one big batch, which sent hundreds of simultaneous users to our site and almost immediately exceeded the free tier capacity (which I believe is limited to 100 simultaneous connections on Firebase). As soon as we realized what was going on, we switched to pay-as-you go so we can scale to an unlimited number of users. This required just a few clicks to select an appropriate plan in the dashboard.
Lesson Four: Automate Preview Deploys
“Works on my machine.” — Every developer, ever.
It seems like this is always an issue. Our site worked great in our local development environments, but when we deployed the site, our CSS completely failed. Everything else worked, but we got unstyled pages. It seems like while we were building the site, some of our dependencies drifted out of compatibility with each other. We were deploying with Zeit’s Now service (highly recommended), and for some reason the production build just wouldn’t play nice with some of the older libraries we were using.
We were hoping to push off the upgrades until after launch, which would have allowed us to launch a few weeks earlier, but eventually we just had to bite the bullet and upgrade everything to get the production build working.
Luckily, we decided early on that we were going to create a production build every time we opened a PR. Using Zeit made that whole process a breeze. They even have a bot that will report the deploy links right into your GitHub PRs. This helped us notice and fix the issue before we tried to actually launch the site.
Lesson Five: Sometimes You Just Have to Build Something New
We looked around for components we could use to run unit tests in the interactive code editor, but we couldn’t find one that would have been easy to integrate — so we built our own. We’d taken a crack at this job before to provide code feedback on DevAnywhere.io, so we knew what we liked and what we didn’t like about other solutions.
We wanted a testing framework with dead simple assertions so we could make every assertion with a single API call. It turns out, we liked writing our unit tests that way so much, we extracted it into a new unit testing framework that we’re sharing with the world. Check out “Rethinking Unit Test Assertions” to learn why we love it so much, and take RITEWay for a test drive on your next project. It’s spoiled us.
Lesson Six: TDD Rocks
I’ve already mentioned that TDD changed my life but it was particularly useful on this project. This was a medium-sized project with a team of six developers working over a span of several months, expanding and maintaining a codebase with tens of thousands of lines of app code (not including libraries). It looks deceptively simple, but the devil is in the details.
Using Test Driven Development gave us the confidence to automate a lot of the maintenance. We used Travis-CI to run our tests on every pull request, and GitHub PRs for code review. We also used Renovate Bot to help us keep our dependencies up-to-date. Having good code coverage meant that we could safely auto-merge most of the Renovate PRs using the following config:
"schedule": "before 4am",
Lesson Seven: Celebrate Wins
We worked a long time on this project and we pushed hard to get it across the finish line over the last several weeks. Big thank you to everybody who helped, and to JS_Cheerleader who vetted the talent and acted as the product manager.
I hope you enjoy using it as much as we enjoyed building it.
What’s Inside EricElliottJS.com?
The new site includes everything that was in the original, including lessons on:
- Functional Programming
- Function Composition
- Currying & Partial Application
- Object Oriented Programming
- Object Composition
- Functional Mixins
- Node & Express
- Shotgun: Watch me code real projects
- Object Composition
- Functional Programming
Dozens of hours of video content. Dozens of code exercises. App projects. Exclusive access to content you can’t find anywhere else.
If you’re not a member yet, join now.
He enjoys a remote lifestyle with the most beautiful woman in the world.