A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.html 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang="fr">
  4. <!-- Has to be within the first 1024 bytes, hence before the `title` element
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset="utf-8">
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name="viewport" content="width=device-width,initial-scale=1">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>Comfort of Bloated Web (archive) — David Larlet</title>
  13. <meta name="description" content="Publication mise en cache pour en conserver une trace.">
  14. <!-- That good ol' feed, subscribe :). -->
  15. <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
  16. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  17. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
  18. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
  19. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
  20. <link rel="manifest" href="/static/david/icons2/site.webmanifest">
  21. <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
  22. <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
  23. <meta name="msapplication-TileColor" content="#f7f7f7">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
  26. <meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
  27. <!-- Documented, feel free to shoot an email. -->
  28. <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
  29. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  30. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  31. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  32. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  35. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  36. <script>
  37. function toggleTheme(themeName) {
  38. document.documentElement.classList.toggle(
  39. 'forced-dark',
  40. themeName === 'dark'
  41. )
  42. document.documentElement.classList.toggle(
  43. 'forced-light',
  44. themeName === 'light'
  45. )
  46. }
  47. const selectedTheme = localStorage.getItem('theme')
  48. if (selectedTheme !== 'undefined') {
  49. toggleTheme(selectedTheme)
  50. }
  51. </script>
  52. <meta name="robots" content="noindex, nofollow">
  53. <meta content="origin-when-cross-origin" name="referrer">
  54. <!-- Canonical URL for SEO purposes -->
  55. <link rel="canonical" href="https://susam.net/maze/wall/comfort-of-bloated-web.html">
  56. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">
  57. <article>
  58. <header>
  59. <h1>Comfort of Bloated Web</h1>
  60. </header>
  61. <nav>
  62. <p class="center">
  63. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  64. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  65. </svg> Accueil</a> •
  66. <a href="https://susam.net/maze/wall/comfort-of-bloated-web.html" title="Lien vers le contenu original">Source originale</a>
  67. </p>
  68. </nav>
  69. <hr>
  70. <p>
  71. There is a tiny
  72. <a href="https://susam.net/form/comment/?post=hello">comment form</a>
  73. application on this website to accept comments from visitors and
  74. save them on the file system of the web server for me to review
  75. later and publish. This form is one of the only two things on this
  76. website that is dynamic in nature. The other dynamic part is
  77. the <a href="https://susam.net/form/subscribe/">subscription form</a>.
  78. Everything on this website other than these two things are static in
  79. nature.
  80. </p>
  81. <h2 id="mysterious-copies">Mysterious Copies<a href="#mysterious-copies"></a></h2>
  82. <p>
  83. Most of this website is made of handcrafted HTML. The blog posts and
  84. other content files are handcrafted HTML pages. A Common Lisp
  85. program adds common headers and footers to these pages and generates
  86. the HTML pages that are served as static files via Nginx running on
  87. a Debian system. The comment form, however, is a dynamic web
  88. application served via another Common Lisp program that makes use of
  89. the Hunchentoot web server to serve the form, accept the input
  90. submitted by the user, and then process it. This comment form is a
  91. very simple, minimal, and stateless application that fulfills the
  92. modest requirements of this modest website pretty well.
  93. </p>
  94. <p>
  95. However, often I see multiple copies of the same comment being saved
  96. on my web server. In the initial days of encountering this issue, I
  97. felt quite confused. I could rule out a bug in my program by
  98. carefully reviewing and testing it. Further, the web server logs
  99. clearly showed multiple POST requests being received by it from the
  100. same client usually with a few seconds of intervals between the
  101. consecutive requests. The comments seemed to have legitimate
  102. content. Since the duplicate copies would all have the same comment,
  103. I would arbitrarily pick one and publish it on my website. But I
  104. often wondered why on earth well meaning visitors would sometimes
  105. submit the same comment multiple times. For good measure? Perhaps!
  106. But still quite odd!
  107. </p>
  108. <h2 id="so-what-is-the-problem">So What's the Problem?<a href="#so-what-is-the-problem"></a></h2>
  109. <p>
  110. The mystery of duplicate comment submission remained a puzzle for
  111. several months. Then one day, one of the visitors to my website
  112. contacted me via Twitter messages to tell me that my comment form
  113. was broken and it was not working for him.
  114. </p>
  115. <p>
  116. The conversation began like this: "Hey! The comment form on your
  117. website seems to be broken. It says it has accepted my comment but I
  118. don't think it is doing that."
  119. </p>
  120. <p>
  121. I responded, "Hi! Thank you for contacting me about this issue. What
  122. do you mean it does not accept your comment? Do you see an error?"
  123. </p>
  124. <p>
  125. "There is no error. In fact, after submitting, I get a success
  126. message that says, 'Comment was submitted successfully. It may be
  127. published after review.'"
  128. </p>
  129. <p>
  130. "That sounds about right. So what's the problem?"
  131. </p>
  132. <p>
  133. In the meantime, I performed some testing at my end to find that the
  134. comment form appeared to be working fine with no apparent issues.
  135. Further, I found that there were multiple copies of his comment
  136. saved neatly on the server for me to review later and publish.
  137. </p>
  138. <p>
  139. Before I could share my findings, he continued, "Well! That success
  140. message appears almost instantly. It couldn't be storing my comment
  141. successfully that fast, could it?"
  142. </p>
  143. <p>
  144. That is when the mystery unfolded for me! The issue was that the
  145. comment form accepts the user's comment and returns a success
  146. message too soon for the user to believe that it could have possibly
  147. saved the comment. I have had a couple of other very similar
  148. conversations since then when a visitor contacted me via email or
  149. another means to double-check if my comment form was really working
  150. fine. In all of these cases, they were skeptical about the success
  151. message because it appeared much sooner than they expected.
  152. </p>
  153. <h2 id="bloated-expectations">Bloated Expectations<a href="#bloated-expectations"></a></h2>
  154. <p>
  155. Depending on where the visitor is located, the comment form on this
  156. website may take anywhere between 30 ms to 900 ms, and very rarely a
  157. little longer, to accept the user's comment, save it successfully,
  158. and then display a success message to the user. But apparently, a
  159. few hundred millseconds is too fast for many people to be able to
  160. trust that the comment application is actually doing its job. I
  161. presume that they have become so used to waiting for at least a
  162. second or more for dynamic pages to load that a web application that
  163. finishes its job in a few milliseconds appears to be fishy.
  164. </p>
  165. <p>
  166. I must clarify here that the duplicate comment submissions do not
  167. bother me at all. The duplicate comments I receive is a very small
  168. fraction of the total number of comments. I just find it interesting
  169. that users can mistrust a simple piece of software that does a
  170. simple thing in a reasonable amount of time. I had one visitor to my
  171. website even say, "I really was expecting a spinning wheel on the
  172. browser tab or some sort of progress indicator to be assured that it
  173. is saving my comment. The instant success message took me by
  174. surprise!" He felt nervous that his comment was not saved and
  175. resubmitted the comment again.
  176. </p>
  177. <p>
  178. As a result of these conversations, I have sometimes even wondered
  179. whether I should add some artificial delay in the comment
  180. application before responding with a success message to satisfy the
  181. expectations of people who are so used to the bloated web. Of
  182. course, I am not actually going to do that. I want to keep it
  183. simple. I do not like adding artificial restrictions to a simple
  184. piece of functionality. Further, I do not mind the duplicate comment
  185. submissions at all. However, I cannot help but remark that the users
  186. of the web today have become so comfortable with the bloated web
  187. that a simple web application without bells and whistles that is
  188. fast and responsive makes them nervous!
  189. </p>
  190. <h2 id="update">Update<a href="#update"></a></h2>
  191. <p>
  192. <strong>Update on 13 Mar 2022:</strong> Many commenters to this post
  193. have suggested that the issue here might not be the fast response of
  194. a successful comment application but instead the fact that the
  195. comment form elements remain intact even after the comment
  196. submission. In their opinion, merely displaying a success message
  197. may not be assuring enough. They further suggested that I should
  198. change the state of the form in some way on successful submission.
  199. Popular recommendations included disabling, clearing, or removing
  200. the form elements entirely on successful submission.
  201. </p>
  202. <p>
  203. These suggestions are valid of course. However, I also think that
  204. these suggestions reinforce the point of my blog post. Users of the
  205. web today are so used to these fancy features and restrictions that
  206. without them one feels unsure if the comment form application has
  207. really done what it is <em>literally</em> saying it has done.
  208. </p>
  209. <p>
  210. Do I really need to consider <em>artificially</em> clearing,
  211. disabling, or removing the form elements and thus removing the
  212. ability to edit a comment again and resubmit it (even if the user
  213. really so desires) with a single click of a button? What if someone
  214. wants to post another comment immediately after the first one? What
  215. if someone wants to edit their previous comment slightly and repost
  216. it? Do I need to force the user to hop through separate links and
  217. buttons to be able to do these simple things? "But every other
  218. website clears, disables, or removes the form elements," you might
  219. rightfully offer as a counterpoint. I know. I know. But why is it so
  220. that the "right" user experience nowadays involves artifically
  221. encumbering the user or hiding the user's own submitted text from
  222. themselves?
  223. </p>
  224. <p>
  225. In fact, in an earlier version of this website, the comment form
  226. application did disable the form elements on a successful comment
  227. submission. In an even older version of this website, a new page
  228. consisting only a success message appeared. However, visitors would
  229. still report their suspicion regarding the comment form doing its
  230. job to me. So I have observed the results of employing at least two
  231. of the measures suggested and they were still consistent with the
  232. reporters' claims that they find the instant success message from
  233. the application to be disconcerting.
  234. </p>
  235. <p>
  236. UX best practices notwithstanding, I do not want to hide the users'
  237. content from themselves after a successful form submission. Maybe
  238. they want to copy the message and keep it for themselves. Maybe they
  239. want to edit it again and repost it. I do not want to restrict any
  240. of these simple things. I also do not want the user to hop through
  241. another additional link or button to be able to do these things.
  242. Therefore, heeding some of the other advice I have received, I have
  243. now made two tiny changes to the comment form application that is
  244. consistent with its original spirit of simplicity, minimalism, and
  245. statelessness. I have moved the success message to the top of the
  246. response page. I have also added additional text in the success
  247. message that explains the submitted input has been left intact in
  248. case the submitter wants to copy it, or edit it and resubmit it.
  249. </p>
  250. </article>
  251. <hr>
  252. <footer>
  253. <p>
  254. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  255. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  256. </svg> Accueil</a> •
  257. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  258. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  259. </svg> Suivre</a> •
  260. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  261. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  262. </svg> Pro</a> •
  263. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  264. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  265. </svg> Email</a> •
  266. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  267. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  268. </svg> Légal</abbr>
  269. </p>
  270. <template id="theme-selector">
  271. <form>
  272. <fieldset>
  273. <legend><svg class="icon icon-brightness-contrast">
  274. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  275. </svg> Thème</legend>
  276. <label>
  277. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  278. </label>
  279. <label>
  280. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  281. </label>
  282. <label>
  283. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  284. </label>
  285. </fieldset>
  286. </form>
  287. </template>
  288. </footer>
  289. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  290. <script>
  291. function loadThemeForm(templateName) {
  292. const themeSelectorTemplate = document.querySelector(templateName)
  293. const form = themeSelectorTemplate.content.firstElementChild
  294. themeSelectorTemplate.replaceWith(form)
  295. form.addEventListener('change', (e) => {
  296. const chosenColorScheme = e.target.value
  297. localStorage.setItem('theme', chosenColorScheme)
  298. toggleTheme(chosenColorScheme)
  299. })
  300. const selectedTheme = localStorage.getItem('theme')
  301. if (selectedTheme && selectedTheme !== 'undefined') {
  302. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  303. }
  304. }
  305. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  306. window.addEventListener('load', () => {
  307. let hasDarkRules = false
  308. for (const styleSheet of Array.from(document.styleSheets)) {
  309. let mediaRules = []
  310. for (const cssRule of styleSheet.cssRules) {
  311. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  312. continue
  313. }
  314. // WARNING: Safari does not have/supports `conditionText`.
  315. if (cssRule.conditionText) {
  316. if (cssRule.conditionText !== prefersColorSchemeDark) {
  317. continue
  318. }
  319. } else {
  320. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  321. continue
  322. }
  323. }
  324. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  325. }
  326. // WARNING: do not try to insert a Rule to a styleSheet you are
  327. // currently iterating on, otherwise the browser will be stuck
  328. // in a infinite loop…
  329. for (const mediaRule of mediaRules) {
  330. styleSheet.insertRule(mediaRule.cssText)
  331. hasDarkRules = true
  332. }
  333. }
  334. if (hasDarkRules) {
  335. loadThemeForm('#theme-selector')
  336. }
  337. })
  338. </script>
  339. </body>
  340. </html>