Hard-Won Lessons Building Maintainable Web Applications
I’ve built web applications for 15 years. Some have succeeded and flourished, others have crashed and burned. But I’ve learned some hard-won lessons along the way: techniques that correlate with maintainable code and long-term success. Maybe they can help you.
1. Write as little JavaScript as possible.
Only write code that you need. Many web application patterns can be accomplished with HTML and CSS. You should do it there, if at all possible. No one wants to use your “beautiful modern input control”.
2. Write boring code.
Writing “as little JavaScript as possible” isn’t about optimizing “lines of code”. It’s about reducing the concepts in your code. More simple code is 10x better than compact clever code.
You want your app filled with code that any novice can pick up, understand, and work with. Because any novice could be the next developer responsible for maintaining it.
3. Use progressive enhancement.
Your app should mostly work without JavaScript. Really. Not because anyone browses the web without JavaScript, but because Scripts fail to load (See #5) and JavaScript often breaks (See #6). When it does, give your users a fallback plan with solid HTML that does traditional form posts against your API.
4. You can’t test every browser. Don’t try.
Chrome, Firefox, Safari, Mobile Safari, Internet Explorer, Edge Pre-Blink, Edge Post-Blink, Facebook embedded, WeChat, Gameboy, and that smart microwave from Samsung. There are too many browsers with too many quirks. You can’t test them all. If you find yourself with flaky code that’s often breaking across browsers, you’re probably being too clever. See #2 and #3.
5. Scripts will fail to load.
The Internet is less reliable than most developers think. Scripts fail to load all the time. As many as 10% of requests fail on slower mobile connections. When one of these failed requests is your dependency <script>
tag, how does your application behave?
Most apps just blow up with an error like jQuery is undefined
.
If your app followed “#3 Use progressive enhancement”, the user would have never noticed, and your app would feel more reliable.
6. Monitor everything.
Your code will break, and you have no idea how. If you did, you would have fixed it already!
Users will do unpredictable things with unpredictable browsers, and you need to know when it happens. Monitoring your website from separate infrastructure gives you the feedback to know when you missed something, and fix it.
7. Keep it simple as long as possible.
Most web applications aren’t big. Some of the most important and valuable apps I’ve ever built are less than 10,000 lines of code. Way less.
Until you have more than 1,000 lines of JavaScript, you don’t need bundlers or webpack or frameworks. You need a script tag pointing to a scripts.js
file. You don’t even need to minify it.
8. Use a modern framework.
If you really need to build something big (see #1, #2, #3, and #7), use a modern framework. Don’t invent one yourself. It probably won’t be quite as good and it’s a huge waste of your time.
But it doesn’t matter which one you pick. It really doesn’t. Throw a dart and go. You’ll likely rebuild the frontend every few years anyway.
9. Server-side code is better than client-side code.
If you can implement a feature in either the client or the server, build it on the server. The server is more stable(#4), more reliable (#5), less error prone, and will likely be viable for much longer than the client-side code (#8).
10. Performance is important
Just because you have a fast device on a good network doesn’t mean your users do. Most users are on older computers, cheaper phones, and more remote networks. If your app isn’t fast, it doesn’t matter what else it does.
Write less JavaScript. Serve fewer, smaller assets. Do work asynchronously on servers or web workers.
You’ll need to see how your users experience the performance of your site. You should monitor real-user performance.
11. The cache will lie to you.
Some browsers will discard their asset caches randomly. Others will hold onto their cache for months with no explanation. You do not control this. Build backward-compatible code and handle unexpected requests.
12. You don’t understand the browser.
Web browsers are incredibly complex and constantly changing. New technologies, UI patterns, draft standards, exception pathways, and extension hooks are being changed all the time with evergreen releases. They will change in subtly different ways.
13. Third-parties will change things when you least want them to.
When you rely on a third-party to host your scripts, provide an API, or provide a service, you’re giving up control of when changes happen. The decisions of other teams can break your app when you least expect it.
If at all possible, host assets yourselves. Have practical fallbacks when external services are offline or different than what you expect.
14. Ad-blockers break everything.
Ad blockers are the worst. In their effort to fight abusive advertising, they have broken code around the web with overly aggressive rules and domain exclusions. Up to 25% of your users have an ad-blocker, with varying levels of strictness. Do you know how your web app works for them?
Monitor your app (#6) and use progressive enhancement (#3).
15. You don’t need to understand everything.
The web development ecosystem is huge, diverse, and changing. You don’t need to understand everything, and no one does. Find tools that work for you and get on with development. It doesn’t matter what anyone else is doing.
I could have saved myself a lot of pain and a lot of long nights if I understood these things earlier in my career. But I guess that’s just getting older and wiser 😀.
What would you add to this list? Let me know on Twitter!