I teach a class over at the local college here in Long Beach and a majority of the content is hosted on the Canvas LMS so students can access it online. And, naturally, I want the content to be as accessible as possible, so thank goodness Canvas has a11y tooling built right into it.
But it ain’t all that rosy. It makes assumptions like all other a11y tooling and adheres to guidelines that were programmed into it. It’s not like the WCAG is baked right in and updated when it updates.
The reason this is even on my mind is that Jeremy yesterday described his love for writing image descriptions:
I enjoy writing alt text. I recently described how I updated my posting interface here on my own site to put a textarea for alt text front and centre for my notes with photos. Since then I’ve been enjoying the creative challenge of writing useful—but also evocative—alt text.
I buy into that! Writing alt text is a challenge that requires a delicate dance between the technical and the creative. It’s both an opportunity to make content more accessible and enhance the user experience.
One of those programmed guidelines in the Canvas tool is a cap of 120 characters on alt text. Why 120? I dunno, I couldn’t find any supporting guideline or rule for that exact number. One answer is that screen readers stop announcing text after 125 characters, but that’s apparently untrue, at least today. The general advice for how long alt text should be comes in varying degrees:
Jake Archibald talks of length in terms of emotion. Detail is great, but too much detail might distort the focal point, which makes total sense.
Carrie Fisher suggests a 150-character limit not because screen readers will truncate them but more as a mental note that maybe things are getting too descriptive.
Daniel Göransson says in this 2017 guide that it comes down to context and knowing when certain details of an image are worth additional explanation. But he generally errs on the side of conciseness.
So, how long should alt text be? The general consensus here is that there is no hard limit, but more of a contextual awareness of what purpose the image serves and adapting to it accordingly.
Which gets me back to Jeremy’s article. He was writing alt text for a group of speaker headshots and realized the text was all starting to sound the same. He paused, thought about the experience, compared it to the experience of a sighted user, and created parity between them:
The more speakers were added to the line-up, the more I felt like I was repeating myself with the alt text. […] The experience of a sighted person looking at a page full of speakers is that after a while the images kind of blend together. So if the alt text also starts to sound a bit repetitive after a while, maybe that’s not such a bad thing. A screen reader user would be getting an equivalent experience.
I dig that. So if you’re looking for a hard and fast rule on character counts, sorry to disappoint. Like so many other things, context is king and that’s the sort of thing that can’t be codified, or even automated for that matter.
And while we’re on the topic, just noticed that Twitter has UI to display alt text:
Now if only there was more contrast between that text and the background… a11y is hard.
No doubt loads of people boned up on these technologies during that time. I also think there is no doubt there are lots of people that haven’t learned much CSS since then.
So what would we tell them?
Some other folks have speculated similarly. Scott Vandehey in “Modern CSS in a Nutshell” wrote about his friend who hasn’t kept up with CSS since about 2015 and doesn’t really know what to learn. I’ll attempt to paraphrase Scott’s list and what’s changed since the days of CSS3.
Preprocessors are still widely used since the day of CSS3, but the reasons to use them have dwindled, so maybe don’t even bother. This includes more newfangled approaches like polyfilling future features. This also includes Autoprefixer. CSS-in-JS is common, but only on projects where the entire workflow is already in JavaScript. You’ll know when you’re on a relevant project and can learn the syntax then if you need to. You should learn Custom Properties, Flexbox, and Grid for sure.
Sounds about right to me. But allow me to make my own list of post-CSS3 goodies that expands upon that list a smidge.
You really gotta learn Flexbox and Grid if you haven’t — they are really cornerstones of CSS development these days. Even more so than any feature we got in CSS3.
Grid is extra powerful when you factor in subgrid and masonry, neither of which is reliable cross-browser yet but probably will be before too long.
Custom properties are also a big deal for several reasons. They can be your home for design tokens on your project, making a project easier to maintain and keep consistent. Color theming is a big use case, like dark mode.
You can go so far as designing entire sites using mostly custom properties. And along those lines, you can’t ignore Tailwind these days. The approach of styling an entire site with classes in HTML strikes the right chord with a lot of people (and the wrong chord with a lot of people, so no worries if it doesn’t jive with you).
Preference queries are generally @media queries like we’ve been using to respond to different browsers sizes for year, but now include ways to detect specific user preferences at the OS level. Two examples are prefers-reduced-motion and prefers-color-scheme. These allow us to build interfaces that more closely respect a user’s ideal experience. Read Una’s post.
.block { background: hsl(0 33% 53% / 0.5); background: rgb(255 0 0); background: /* can display colors no other format can */ color(display-p3 0.9176 0.2003 0.1386) background: lab(52.2345% 40.1645 59.9971 / .5);} background: hwb(194 0% 0% / .5); }
Color Changes
The color syntax is moving to functions that accept alpha (transparency) without having the change the function name. For example, if you wanted pure blue in the CSS3 days, you might do rgb(0, 0, 255). Today, however, you can do it no-comma style (both work): rgb(0 0 255), and then add alpha without using a different function: rgb(0 0 255 / 0.5). Same exact situation for hsl(). Just a small nicety, and how future color functions will only work.
Web fonts became a big thing in CSS3. Now there are variable fonts. You might as well know they exist. They both unlock some cool design possibilities and can sometimes be good for performance (like no longer needing to load different font files for bold and italic versions of the same font, for example). There is a such thing as color fonts too, but I’d say they haven’t seen much popularity on the web, despite the support.
.mask { mask: url(mask.png) right bottom / 100px repeat-y; }
.move-me { offset-path: path('M 5 5 m -4, 0 a 4,4 0 1,0 8,0 a 4,4 0 1,0 -8,0'); animation: move 3s linear infinite; } @keyframes move { 100% { offset-distance: 100%; } }
Paths
SVG has also exploded since CSS3. You can straight up crop any element into shapes via clip-path, bringing SVG-like qualities to CSS. Not only that, but you can animate elements along paths, float elements along paths, and even update the paths of SVG elements.
These all feel kind of spirtually connected to me:
clip-path — allows us to literally crop elements into shapes.
masks — similar to clipping, but a mask can have other qualities like being based on the alpha channel of the mask.
There is a lot of image manipulation (not to mention other DOM elements) that is possible these days directly in CSS. There is quite literally filter, but its got friends and they all have different uses.
These all feel kind of spirtually connected to me:
filter — all sorts of useful Photoshop-like effects like brightness, contrast, grayscale, sautration, etc. Blurring is a really unique power.
background-blend-mode — again, evocative of Photoshop in how you can blend layers. Multiply the layers to darken and combine. Overlay to mix the background and color. Lighten and darken are classic effects that have real utility in web design, and you never know when a more esoteric lighting effect will create a cool look.
backdrop-filter — the same abilities you have with filter, but they only apply to the background and not the entire element. Blurring just the background is a particularly useful effect.
mix-blend-mode — the same abilities you have with background-blend-mode, but for the entire element rather than bring limited to backgrounds.
import "https://unpkg.com/extra.css/confetti.js";
body { background: paint(extra-confetti); height: 100vh; margin: 0; }
Houdini
Houdini is really a collection of technologies that are all essentially based around extending CSS with JavaScript, or at least at the intersection of CSS and JavaScript.
Paint API — returns an image that is built from <canvas> APIs and can be controlled through custom properties.
Properties & Values API / Typed OM — gives types to values (e.g. 10px) that would have otherwise been strings.
Layout API — create your own display properties.
Animation API
Combined, these make for some really awesome demos, though browser support is scattered. Part of the magic of Houdini is that it ships as Worklets that are pretty easy to import and use, so it has the potential to modularize powerful functionality while making it trivially easy to use.
The Shadow DOM comes up a bit if you’ve played with <svg> and the <use> element. The “cloned” element that comes through has a shadow DOM that has limitations on how you can select “through” it. Then, when you get into <web-components>, it’s the same ball of wax.
It’s notable that the CSS working group has its own way of drawing lines in the sand year-to-year, noting where certain specs are at a given point in time:
These are pretty dense though. Sure, they’re great references and document things where we can see what’s changed since CSS3. But no way I’d send a casual front-end developer to these to choose what to learn.
Yeah — but what’s coming?
I’d say probably don’t worry about it. 😉
The point of this is catching up to useful things to know now since the CSS3 era. But if you’re curious about what the future of CSS holds in store…
Container queries will be a huge deal. You’ll be able to make styling choices based on the size of a container element rather than the browser size alone. And it’s polyfillable today.
Container unitswill be useful for sizing things based on the size of a container element.
Independant transforms, e.g. scale: 1.2;, will feel more logical to use than always having to use transform.
Nesting is a feature that all CSS preprocessor have had forever and that developers clearly like using, particularly for media queries. It’s likely we’ll get it in native CSS soon.
Scoping will be a way to tell a block of CSS to only apply to a certain area (the same way CSS-in-JS libraries do), and helps with the tricky concept of proximity.
Viewport units will greatly improve with the introduction of “relative” viewport lengths. The super useful ones will be dvh and dvw, as they factor in the actual usable space in a browser window, preventing terrible issues like the browser UI overlapping a site’s UI.
Bramus Van Damme has a pretty good article covering these things and more in his “CSS in 2022” roundup. It looks like 2022 should be a real banner year for CSS. Perhaps more of a banner year than the CSS3 of 2015.
CSS overrides can change the default look of almost anything:
You can use CSS to override what a checkbox or radio button looks like, but if you don’t, the checkbox will look like a default checkbox on your operating system and some would say that’s best for accessibility and usability.
You can use CSS to override what a select menu looks like, but if you don’t, the select will look like a default select menu on your operating system and some would say that’s best for accessibility and usability.
You can override what anchor links look like, but some would say they should be blue with underlines because that is the default and it’s best for accessibility and usability.
You can override what scrollbars look like, but if you don’t, the scrollbars will look (and behave) the way default scrollbars do on your operating system, and some would say that’s best for accessibility and usability.
In my experience, everyone has a different line. Nearly everybody styles their buttons. Nearly everybody styles their links, but some might only customize the hue of blue and leave the underline, drawing the line at more elaborate changes. It’s fairly popular to style form elements like checkboxes, radio buttons, and selects, but some people draw the line before that.
Some people draw a line saying you should never change a default cursor, some push that line back to make the cursor into a pointer for created interactive elements, some push that line so far they are OK with custom images as cursors. Some people draw the line with scrollbars saying they should never be customized, while some people implement elaborate designs.
CSS is a language for changing the design of websites. Every ruleset you write likely changes the defaults of something. The lines are relatively fuzzy, but I’d say there is nothing in CSS that should be outright banned from use — it’s more about the styling choices you make. So when you do choose to style something, it remains usable and accessible. Heck, background-color can be terribly abused making for inaccessible and unusable areas of a site, but nobody raises pitchforks over that.
Container queries are going to solve this long-standing issue in web design where we want to make design choices based on the size of an element (the container) rather than the size of the entire page. So, if a container is 600px wide, perhaps it has a row-like design, but any narrower than that it has a column-like design, and we’ll have that kind of control. That’s much different than transitioning between layouts based on screen size.
We can already size some things based on the size of an element, thanks to the % unit. For example, all these containers are 50% as wide as their parent container.
The % here is 1-to-1 with the property in use, so width is a % of width. Likewise, I could use % for font-size, but it will be a % of the parent container’s font-size. There is nothing that lets me cross properties and set the font-size as a % of a container’s width.
That is, unless we get container units! Here’s the table of units per the draft spec:
unit
relative to
qw
1% of a query container’s width
qh
1% of a query container’s height
qi
1% of a query container’s inline size
qb
1% of a query container’s block size
qmin
The smaller value of qi or qb
qmax
The larger value of qi or qb
With these, I could easily set the font-size to a percentage of the parent container’s width. Or line-height! Or gap! Or margin! Or whatever!
Miriam notes that we can actually play with these units right now in Chrome Canary, as long as the container queries flag is on.
🥳 oh, and also Chrome Canary (with the container query flag) now has support for Query Units:https://t.co/GkIbLFW5C6
Not sure when it happened – so far I've only tested qi, because that's the most useful.
If I may quote @chriscoyier… "CSS comes at you fast"
I had a quick play too. I’ll just put a video here as that’ll be easier to see in these super early days.
And some great exploratory work from Scott here as well:
Developed a demo using the new container units and it’s really powerful stuff! Just two font size declarations and <100 lines of CSS in this demo and it has a ton of flexibility.https://t.co/LlrHFhVND0
Query units can save us effort and time when dealing with things like font-size, padding, and margin within a component. Instead of manually increasing the font size, we can use query units instead.
Both are essentially database-backed systems for managing data. HubSpot is both, and much more. Where a CMS might be very focused on content and the metadata around making content useful, a CRM is focused on leads and making communicating with current and potential customers easier.
They can be brothers-in-arms. We’ll get to that.
Say a CRM is set up for people. You run a Lexus dealership. There is a quote form on the website. People fill it out and enter the CRM. That lead can go to your sales team for taking care of that customer.
But a CRM could be based on other things. Say instead of people it’s based on real estate listings. Each main entry is a property, with essentially metadata like photos, address, square footage, # of bedrooms/baths, etc. Leads can be associated with properties.
That would be a nice CRM setup for a real estate agency, but the data that is in that CRM might be awfully nice for literally building a website around those property listings. Why not tap into that CRM data as literal data to build website pages from?
That’s what I mean by a CRM and CMS being brothers-in-arms. Use them both! That’s why HubSpot can be an ideal home for websites like this.
To keep that tornado of synergy going, HubSpot can also help with marketing, customer service, and integrations. So there is a lot of power packed into one platform.
And with that power, also a lot of comfort and flexibility.
You’re still developing locally.
You’re still using Git.
You can use whatever framework or site-building tools you want.
You’ve got a CLI to control things.
There is a VS Code Extension for super useful auto-complete of your data.
There is a staging environment.
And the feature just keep coming. HubSpot really has a robust set of tools to make sure you can do what you need to do.
Do you have to use some third-party thing for search? Nope, they got it.
As developer-rich as this all is, it doesn’t mean that it’s developer-only. There are loads of tools for working with the website you build that require no coding at all. Dashboard for content management, data wrangling, style control, and even literal drag-and-drop page builders.
It’s all part of a very learnable system.
Themes, templates, modules, and fields are the objects you’ll work with most in HubSpot CMS as a developer. Using these different objects effectively lets you give content creators the freedom to work and iterate on websites independently while staying inside style and layout guardrails you set.
I think that the above additions could help to educate developers about CSS tremendously. The only downside I can think of is that additional information might overwhelm developers, but I would take that risk in favor of more people learning CSS properly.
I’d be for it. The crossed-off UI for the “losing” selectors is attempting to teach this, but without actually teaching it. I wouldn’t be that worried about the information being overwhelming. I think if they are considerate about the design, it can be done tastefully. DevTools is a very information-dense place anyway.
Removing media support from HTML video was a mistake. It means that for every video we embed in HTML, we’re stuck with the choice of serving source files that are potentially too large or small for many users’ devices (resulting in poor performance, wasteful data consumption, and even sub-optimal quality on larger screens), or resorting to more complicated server-side or scripted or third-party solutions to deliver a correct size.
I remember when responsive images were just starting to come out. One way to explain it was to say it’s like <video> in that you can have multiple <source> elements inside which (in supporting browsers) allowed you to specify attributes like type (e.g. video format) and media (e.g. screen size). But then…
Despite being implemented in multiple browsers, the feature was removed from browsers and the HTML specification, without any proposed replacement for the functionality it once provided. One exception is the feature was never removed from Webkit, so it still works in Safari browsers, which is great.
I don’t remember that. That feels like a big WTF moment (some background). I think of the web as being tremendous at backwards compatibility. It’s a rare day when we just yank stuff, and even more rare is a yanking with no alternative whatsoever.
So now with responsive images being a success (it’s a success, right? I can’t imagine how incredibly much bandwidth it has saved the world)… can’t we… put it back?
When I have an immediate need for this, I always think of Cloudinary, because I can alter the size and format of video by changing the URL. Like here’s a video URL where the video codec is automatically determined and the size is forced down to 400px:
Matt Hobbs says you should hire a front-end developer because…
“A front-end developer is the best person to champion accessibility best practices in product teams.”
“80-90% of the end-user response time is spent on the front end.”
“A front-end developer takes pressure off interaction designers.”
“If you do not have a front-end developer there is a high risk that the good work the rest of the team does will not be presented to users in the best way.”
More than ever, new products aim to make an impact on a global scale, and user experience is rapidly becoming the determining factor for whether they are successful or not. These properties of your application can significantly influence the user experience:
Performance & low latency
The application does what you expect
Security
Features and UI
Let’s begin our quest toward the perfect user experience!
1) Performance & Low Latency
Others have said it before; performance is user experience (1, 2). When you have caught the attention of potential visitors, a slight increase in latency can make you lose that attention again.
2) The application does what you expect
What does ‘does what you expect’ even mean? It means that if I change my name in my application to ‘Robert’ and reload the application, my name will be Robert and not Brecht. It seems important that an application delivers these guarantees, right?
Whether the application can deliver on these guarantees depends on the database. When pursuing low latency and performance, we end up in the realm of distributed databases where only a few of the more recent databases deliver these guarantees. In the realm of distributed databases, there might be dragons, unless we choose a strongly (vs. eventually) consistent database. In this series, we’ll go into detail on what this means, which databases provide this feature called strong consistency, and how it can help you build awesomely fast apps with minimal effort.
3) Security
Security does not always seem to impact user experience at first. However, as soon as users notice security flaws, relationships can be damaged beyond repair.
4) Features and UI
Impressive features and great UI have a great impact on the conscious and unconscious mind. Often, people only desire a specific product after they have experienced how it looks and feels.
If a database saves time in setup and configuration, then the rest of our efforts can be focused on delivering impressive features and a great UI. There is good news for you; nowadays, there are databases that deliver on all of the above, do not require configuration or server provisioning, and provide easy to use APIs such as GraphQL out-of-the-box.
What is so different about this new breed of databases? Let’s take a step back and show how the constant search for lower latency and better UX, in combination with advances in database research, eventually led to a new breed of databases that are the ideal building blocks for modern applications.
The Quest for distribution
I. Content delivery networks
As we mentioned before, performance has a significant impact on UX. There are several ways to improve latency, where the most obvious is to optimize your application code. Once your application code is quite optimal, network latency and write/read performance of the database often remain the bottleneck. To achieve our low latency requirement, we need to make sure that our data is as close to the client as possible by distributing the data globally. We can deliver the second requirement (write/read performance) by making multiple machines work together, or in other words, replicating data.
Distribution leads to better performance and consequently to good user experience. We’ve already seen extensive use of a distribution solution that speeds up the delivery of static data; it’s called a Content Delivery Network (CDN). CDNs are highly valued by the Jamstack community to reduce the latency of their applications. They typically use frameworks and tools such as Next.js/Now, Gatsby, and Netlify to preassemble front end React/Angular/Vue code into static websites so that they can serve them from a CDN.
Unfortunately, CDNs aren’t sufficient for every use case, because we can’t rely on statically generated HTML pages for all applications. There are many types of highly dynamic applications where you can’t statically generate everything. For example:
Applications that require real-time updates for instantaneous communication between users (e.g., chat applications, collaborative drawing or writing, games).
Applications that present data in many different forms by filtering, aggregating, sorting, and otherwise manipulating data in so many ways that you can’t generate everything in advance.
II. Distributed databases
In general, a highly dynamic application will require a distributed database to improve performance. Just like a CDN, a distributed database also aims to become a global network instead of a single node. In essence, we want to go from a scenario with a single database node…
…to a scenario where the database becomes a network. When a user connects from a specific continent, he will automatically be redirected to the closest database. This results in lower latencies and happier end users.
If databases were employees waiting by a phone, the database employee would inform you that there is an employee closer by, and forward the call. Luckily, distributed databases automatically route us to the closest database employee, so we never have to bother the database employee on the other continent.
Distributed databases are multi-region, and you always get redirected to the closest node.
Besides latency, distributed databases also provide a second and a third advantage. The second is redundancy, which means that if one of the database locations in the network were completely obliterated by a Godzilla attack, your data would not be lost since other nodes still have duplicates of your data.
Distributed databases provide redundancy which can save your application when things go wrong. Distributed databases divide the load by scaling up automatically when the workload increases.
Last but not least, the third advantage of using a distributed database is scaling. A database that runs on one server can quickly become the bottleneck of your application. In contrast, distributed databases replicate data over multiple servers and can scale up and down automatically according to the demands of the applications. In some advanced distributed databases, this aspect is completely taken care of for you. These databases are known as “serverless”, meaning you don’t even have to configure when the database should scale up and down, and you only pay for the usage of your application, nothing more.
Distributing dynamic data brings us to the realm of distributed databases. As mentioned before, there might be dragons. In contrast to CDNs, the data is highly dynamic; the data can change rapidly and can be filtered and sorted, which brings additional complexities. The database world examined different approaches to achieve this. Early approaches had to make sacrifices to achieve the desired performance and scalability. Let’s see how the quest for distribution evolved.
Traditional databases’ approach to distribution
One logical choice was to build upon traditional databases (MySQL, PostgreSQL, SQL Server) since so much effort has already been invested in them. However, traditional databases were not built to be distributed and therefore took a rather simple approach to distribution. The typical approach to scale reads was to use read replicas. A read replica is just a copy of your data from which you can read but not write. Such a copy (or replica) offloads queries from the node that contains the original data. This mechanism is very simple in that the data is incrementally copied over to the replicas as it comes in.
Due to this relatively simple approach, a replica’s data is always older than the original data. If you read the data from a replica node at a specific point in time, you might get an older value than if you read from the primary node. This is called a “stale read”. Programmers using traditional databases have to be aware of this possibility and program with this limitation in mind. Remember the example we gave at the beginning where we write a value and reread it? When working with traditional database replicas, you can’t expect to read what you write.
You could improve the user experience slightly by optimistically applying the results of writes on the front end before all replicas are aware of the writes. However, a reload of the webpage might return the UI to a previous state if the update did not reach the replica yet. The user would then think that his changes were never saved.
The first generation of distributed databases
In the replication approach of traditional databases, the obvious bottleneck is that writes all go to the same node. The machine can be scaled up, but will inevitably bump into a ceiling. As your app gains popularity and writes increase, the database will no longer be fast enough to accept new data. To scale horizontally for both reads and writes, distributed databases were invented. A distributed database also holds multiple copies of the data, but you can write to each of these copies. Since you update data via each node, all nodes have to communicate with each other and inform others about new data. In other words, it is no longer a one-way direction such as in the traditional system.
However, these kinds of databases can still suffer from the aforementioned stale reads and introduce many other potential issues related to writes. Whether they suffer from these issues depends on what decision they took in terms of availability and consistency.
This first generation of distributed databases was often called the “NoSQL movement”, a name influenced by databases such as MongoDB and Neo4j, which also provided alternative languages to SQL and different modeling strategies (documents or graphs instead of tables). NoSQL databases often did not have typical traditional database features such as constraints and joins. As time passed, this name appeared to be a terrible name since many databases that were considered NoSQL did provide a form of SQL. Multiple interpretations arose that claimed that NoSQL databases:
do not provide SQL as a query language.
do not only provide SQL (NoSQL = Not Only SQL)
do not provide typical traditional features such as joins, constraints, ACID guarantees.
model their data differently (graph, document, or temporal model)
Some of the newer databases that were non-relational yet offered SQL were then called “NewSQL” to avoid confusion.
Wrong interpretations of the CAP theorem
The first generation of databases was strongly inspired by the CAP theorem, which dictates that you can’t have both Consistency and Availability during a network Partition. A network partition is essentially when something happens so that two nodes can no longer talk to each other about new data, and can arise for many reasons (e.g., apparently sharks sometimes munch on Google’s cables). Consistency means that the data in your database is always correct, but not necessarily available to your application. Availability means that your database is always online and that your application is always able to access that data, but does not guarantee that the data is correct or the same in multiple nodes. We generally speak of high availability since there is no such thing as 100% availability. Availability is mentioned in digits of 9 (e.g. 99.9999% availability) since there is always a possibility that a series of events cause downtime.
Visualization of the CAP theorem, a balance between consistency and availability in the event of a network partition. We generally speak of high availability since there is no such thing as 100% availability.
But what happens if there is no network partition? Database vendors took the CAP theorem a bit too generally and either chose to accept potential data loss or to be available, whether there is a network partition or not. While the CAP theorem was a good start, it did not emphasize that it is possible to be highly available and consistent when there is no network partition. Most of the time, there are no network partitions, so it made sense to describe this case by expanding the CAP theorem into the PACELC theorem. The key difference is the three last letters (ELC) which stand for Else Latency Consistency. This theorem dictates that if there is no network partition, the database has to balance Latency and Consistency.
According to the PACELC theorem, increased consistency results in higher latencies (during normal operation).
In simple terms: when there is no network partition, latency goes up when the consistency guarantees go up. However, we’ll see that reality is still even more subtle than this.
How is this related to User Experience?
Let’s look at an example of how giving up consistency can impact user experience. Consider an application that provides you with a friendly interface to compose teams of people; you drag and drop people into different teams.
Once you drag a person into a team, an update is triggered to update that team. If the database does not guarantee that your application can read the result of this update immediately, then the UI has to apply those changes optimistically. In that case, bad things can happen:
The user refreshes the page and does not see his update anymore and thinks that his update is gone. When he refreshes again, it is suddenly back.
The database did not store the update successfully due to a conflict with another update. In this case, the update might be canceled, and the user will never know. He might only notice that his changes are gone the next time he reloads.
This trade-off between consistency and latency has sparked many heated discussions between front-end and back-end developers. The first group wanted a great UX where users receive feedback when they perform actions and can be 100% sure that once they receive this feedback and respond to it, the results of their actions are consistently saved. The second group wanted to build a scalable and performant back end and saw no other way than to sacrifice the aforementioned UX requirements to deliver that.
Both groups had valid points, but there was no golden bullet to satisfy both. When the transactions increased and the database became the bottleneck, their only option was to go for either traditional database replication or a distributed database that sacrificed strong consistency for something called “eventual consistency”. In eventual consistency, an update to the database will eventually be applied on all machines, but there is no guarantee that the next transaction will be able to read the updated value. In other words, if I update my name to “Robert”, there is no guarantee that I will actually receive “Robert” if I query my name immediately after the update.
Consistency Tax
To deal with eventual consistency, developers need to be aware of the limitations of such a database and do a lot of extra work. Programmers often resort to user experience hacks to hide the database limitations, and back ends have to write lots of additional layers of code to accommodate for various failure scenarios. Finding and building creative solutions around these limitations has profoundly impacted the way both front- and back-end developers have done their jobs, significantly increasing technical complexity while still not delivering an ideal user experience.
We can think of this extra work required to ensure data correctness as a “tax” an application developer must pay to deliver good user experiences. That is the tax of using a software system that doesn’t offer consistency guarantees that hold up in todays webscale concurrent environments. We call this the Consistency Tax.
Thankfully, a new generation of databases has evolved that does not require you to pay the Consistency Tax and can scale without sacrificing consistency!
The second generation of distributed databases
A second generation of distributed databases has emerged to provide strong (rather than eventual) consistency. These databases scale well, won’t lose data, and won’t return stale data. In other words, they do what you expect, and it’s no longer required to learn about the limitations or pay the Consistency Tax. If you update a value, the next time you read that value, it always reflects the updated value, and different updates are applied in the same temporal order as they were written. FaunaDB, Spanner, and FoundationDB are the only databases at the time of writing that offer strong consistency without limitations (also called Strict serializability).
The PACELC theorem revisited
The second generation of distributed databases has achieved something that was previously considered impossible; they favor consistency and still deliver low latencies. This became possible due to intelligent synchronization mechanisms such as Calvin, Spanner, and Percolator, which we will discuss in detail in article 4 of this series. While older databases still struggle to deliver high consistency guarantees at lower latencies, databases built on these new intelligent algorithms suffer no such limitations.
Database designs influence the attainable latency at high consistency greatly.
Since these new algorithms allow databases to provide both strong consistency and low latencies, there is usually no good reason to give up consistency (at least in the absence of a network partition). The only time you would do this is if extremely low write latency is the only thing that truly matters, and you are willing to lose data to achieve it.
intelligent algorithms result in strong consistency and relatively low latencies
Are these databases still NoSQL?
It’s no longer trivial to categorize this new generation of distributed databases. Many efforts are still made (1, 2) to explain what NoSQL means, but none of them still make perfect sense since NoSQL and SQL databases are growing towards each other. New distributed databases borrow from different data models (Document, Graph, Relational, Temporal), and some of them provide ACID guarantees or even support SQL. They still have one thing in common with NoSQL: they are built to solve the limitations of traditional databases. One word will never be able to describe how a database behaves. In the future, it would make more sense to describe distributed databases by answering these questions:
Is it strongly consistent?
Does the distribution rely on read-replicas, or is it truly distributed?
What data models does it borrow from?
How expressive is the query language, and what are its limitations?
Conclusion
We explained how applications can now benefit from a new generation of globally distributed databases that can serve dynamic data from the closest location in a CDN-like fashion. We briefly went over the history of distributed databases and saw that it was not a smooth ride. Many first-generation databases were developed, and their consistency choices–which were mainly driven by the CAP theorem–required us to write more code while still diminishing the user experience. Only recently has the database community developed algorithms that allow distributed databases to combine low latency with strong consistency. A new era is upon us, a time when we no longer have to make trade-offs between data access and consistency!
At this point, you probably want to see concrete examples of the potential pitfalls of eventually consistent databases. In the next article of this series, we will cover exactly that. Stay tuned for these upcoming articles:
Build a site on a single domain, but detect mobile, and render a separate mobile site
Build a separate mobile site on a subdomain
It’s funny how quickly huge industry-defining conversations fade from view. This was probably the biggest question in web design and development1 this past decade, and we came up with an answer: It’s #1, you should build a responsive website. Any other answer and you’re building multiple websites and the pain from that comes from essentially doubling the workload, splitting teams, communication problems across those teams, inconsistencies across the sites, and an iceberg of other pain points this industry has struggled with for ages. As for the design of the website, you can use Candy Marketing’s Services.
But, the web is a big place.
This emailer specifically mentioned imdb.com as their example. IMDB is an absolutely massive site with a large team (they are owned by Amazon) and lots of money flying around. If the IMDB team decides they would be better off building multiple websites, well that’s their business. They’ve got the resources to do whatever the hell they want.