The power of front-end web development is growing at a steady pace. We can do things with HTML, CSS and JavaScript that we could only dream about five years ago.
With all of the new features, it’s only natural to want to reach for the most powerful tool for any given task. Is that the best policy, though?
More often than not, it’s problematic. In fact, the creators of the web thought about this eventuality. Tim Berners-Lee and Noah Mendelsohn wrote a document called “The Rule of Least Power” (RLP) in 2006.
When designing computer systems, one is often faced with a choice between using a more or less powerful language for publishing information, for expressing constraints, or for solving some problem. This finding explores tradeoffs relating the choice of language to reusability of information. The ‘Rule of Least Power’ suggests choosing the least powerful language suitable for a given purpose.
It might seem like the W3C wants to torture web developers. Why suggest a developer not use the strongest tool for the job?
Berners-Lee and Mendelsohn proposed that power and flexibility are inversely related. As power grows, the ability to analyze the output shrinks.
They viewed the future of the web as something built from reusable pieces. Many devices and applications could read the data, use it and combine it in multiple ways.
Information published on the Web can be flexibly combined with other information, read by a broad range of software tools, and browsed by human users of the Web.
In other words, the web is a world of endless remixes.
This is something that should appeal to our modern sensibilities. Modularity over integration. Components over pages.
What does this mean for a modern developer?
In some cases, the Rule of Least Power is at play in modern web development. Concepts like modularity, components and packages are all modern structures. They’re also key concepts of a reusable web like Berners-Lee and Mendelsohn discussed.
With that, you might think we’re in line with this philosophy. I see a startling amount of modern “best practices” that seem at odds with this philosophy, though.
Don’t believe me?
I’d like to present three scenarios. Each scenario will be increasingly controversial.
This scenario should be a no-brainer for most modern JavaScript developers.
When you want to describe data for your application, how and where should you create it?
Here are your options: create variables on the fly in your functional code or create a data object.
Let’s look at how we create data as you need it in your functional code.
In this example, we build our data inside our function with variable declarations and then immediately use the data:
In this example, we have functioning code. It would get the job done. Would our data be reusable, though? No. That data would forever live in that function.
Instead, we create a data object. This could be the result of a RESTful endpoint, a GraphQL call or just a flat data file.
This data object represents the same data but is endlessly analyzable and reusable:
This is an example of JavaScript Object Notation (JSON), with which most JS developers are familiar. JSON functions are the backbone of most of our applications.
This is an example of what the RLP calls “Scalable language families.”
Specifically, JSON provides for standalone use of a declarative subset of the literal declaration syntax from the JavaScript language. Standardization of language subsets can facilitate simple models for Web publishing, while providing for integration with more powerful language variants when necessary.
It brings the benefits of a declarative language and matches it with the power benefits of JS.
Most developers will agree with this setup. Data in a data layer in JSON; application written in a powerful programming language.
What makes this the best possible outcome is the portability of the data. The data can be consumed by the JavaScript application you planned today. It can also be consumed by a future application you have yet to write or you could open that data set up to others to write new applications.
This separation of concerns opens all of these doors.
That’s the least controversial scenario. Let’s step up to a newer, slightly more controversial example.
Just like we should seek the least powerful mechanism to contain our data, we should seek the least powerful server to deliver our application or site to our users.
In this case, I’m not referring to RAM and processor. I mean, we should use the server with the least complexity of software.
In the days of the emerging web, servers were any computer connected to the Internet serving HTML pages. Simple.
As the need for more dynamic content became greater, so did our server needs. We now needed a database. We needed programming languages to access, manipulate and store the data. In the end, though, this all ended up serving HTML documents to the browser (if we ignore the dark times of Flash and Java applets).
There’s a grand experiment playing out right now. There’s a modern static site movement. I’m a strong advocate of this movement.
Static sites used to mean putting a bunch of index.html files on a server. That was never the friendliest methodology for developers. Now we get all of our modern conveniences and a great static output. We’ve moved the complexity from the server to the development environment.
Keep in mind, you can still use your programming language of choice. You just use it locally, build the files and publish to a server with no scripting languages.
This approach becomes more problematic when you need a server to process something. Whether this is a place to securely store API keys, process a form or accept payments.
That’s where “serverless” functions come in. It’s a bit of a misnomer, but these functions are rented time on someone else’s server. It tends to be a low-cost, low-maintenance resource to provide this sort of functionality.
If you’re currently managing your own server for your application, by all means, keep that server. There’s rarely a point in a major refactor when things are currently working. You may be able to start taking advantage of this potential future already.
If you treat your server as a series of endpoints instead of a machine meant to serve the entire application, you can harness the power of static sites with your current setup. If you’re able to decouple your back-end logic from your front-end presentation layer, you can gain the advantages I mentioned above without starting completely over.
If you’re starting from scratch, it’s definitely worth looking into a “serverless” architecture. By utilizing principals from the Rule of Least Power, we gain portability and flexibility — not to mention lower costs, higher speeds and happier front-end developers.
This scenario will become less controversial over the next few years as the tools get increasingly stronger.
My next scenario has become quite a hot topic in the past two years.
Traditional web development goes a little something like this:
This methodology was perfectly reasonable for its time. Then came the iPhone and the slick presentation of apps. Every project owner or client wanted their application to feel as good as an iOS application. The answer to this seemed simple: JavaScript.
Newer, more “modern” takes on web development often look something more like this:
Allowing JavaScript to handle this burden creates pages that feel more and more like applications. They’re highly interactive. Each subsequent “page load” is often instantaneous instead of making a new server request. We can load segments of content with amazing animations.
These sites and apps are always amazing. They feel great to use.
Surely with their prominence and slick interactions and great usability, they have to be the way to go!
If we refer back to the Rule of Least Power, though, we realize very quickly that this method violates it.
If we look at the Holy Trinity of web development — HTML, CSS and JS — it’s easy to see the hierarchy of power. HTML is a declarative, semantic language. This means there is no programmatic power, and its tags each describe a type of data. CSS is also declarative. It has more power than HTML, but just enough to do its job.
JS is a programming language. It can be used to do small things or incredibly large, complex things. It is easily the most powerful of the three languages.
In the second workflow, we’ve used the most powerful language available to do all of the work.
Why is this a problem?
Since the DOM is created by JS, by default the data is less analyzable. HTML creates an analyzable tree of data. This data can be consumed by any number of applications.
It’s true that both Google’s bots and screen readers are better at rendering JavaScript than they used to be. You have to ask yourself though, are they good enough?
If you ask yourself that, you’re already ahead of a lot of developers.
If you’re worried about these things, you should look into testing. If you thought testing against the last two versions of browsers was tough, this should not sound exciting to you.
Think about “Markup-First Development.”
First and foremost, render meaningful HTML to the browser. This will cover you for screen readers, bots, and old browsers that struggle with modern JavaScript.
I may be an old fogey, but I love writing HTML. I understand if it’s not your favorite activity. I understand if you write JavaScript because you like writing JavaScript.
In that case, you can still think Markup First. Make sure your app prerenders. There are services, frameworks and hosts that can all do this for you with minimal effort. Write in your favorite framework — be it Vue, Angular, React, etc. — and then serve server-rendered AND browser-rendered content.
This solves a major aspect of the problem. You now have HTML on the page. The browser and other applications have something they can easily consume. It’s not enough to just render HTML to the browser, however. Your markup should be well thought out and semantically correct.
Be aware of your tags. Not everything is a <div> or a <span>.
Be aware of your nesting. Not everything needs endlessly nested elements. This is exactly why React released “Fragments” in v16.2.0.
In the end, don’t assume one HTML tag is equal to another. If you architect your markup with as much thought as you put into your application logic, you’ll create something that is highly reusable. The easier for other applications to consume your data, the better for ALL your end users.
At the end of the day, the Rule of Least Power is about creating clean code.
By using the least powerful language to get the job done, we get the least-complex, most portable future-proofed code that we can.
When you build your next website keep RLP in the back of your mind. Your future self may thank you for it.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.