A place to cache linked articles (think custom and personal wayback machine)
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

index.html 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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>
  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>Datasette: A Developer, a Shower and a Data-Inspired Moment (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="#f0f0ea">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f0f0ea">
  26. <!-- Documented, feel free to shoot an email. -->
  27. <link rel="stylesheet" href="/static/david/css/style_2020-06-19.css">
  28. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  29. <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>
  30. <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>
  31. <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>
  32. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  35. <script type="text/javascript">
  36. function toggleTheme(themeName) {
  37. document.documentElement.classList.toggle(
  38. 'forced-dark',
  39. themeName === 'dark'
  40. )
  41. document.documentElement.classList.toggle(
  42. 'forced-light',
  43. themeName === 'light'
  44. )
  45. }
  46. const selectedTheme = localStorage.getItem('theme')
  47. if (selectedTheme !== 'undefined') {
  48. toggleTheme(selectedTheme)
  49. }
  50. </script>
  51. <meta name="robots" content="noindex, nofollow">
  52. <meta content="origin-when-cross-origin" name="referrer">
  53. <!-- Canonical URL for SEO purposes -->
  54. <link rel="canonical" href="https://thenewstack.io/datasette-a-developer-a-shower-and-a-data-inspired-moment/">
  55. <body class="remarkdown h1-underline h2-underline h3-underline hr-center ul-star pre-tick">
  56. <article>
  57. <header>
  58. <h1>Datasette: A Developer, a Shower and a Data-Inspired Moment</h1>
  59. </header>
  60. <nav>
  61. <p class="center">
  62. <a href="/david/" title="Aller à l’accueil">🏠</a> •
  63. <a href="https://thenewstack.io/datasette-a-developer-a-shower-and-a-data-inspired-moment/" title="Lien vers le contenu original">Source originale</a>
  64. </p>
  65. </nav>
  66. <hr>
  67. <main>
  68. <p>We tend to think of open source as a community, even as an altruistic endeavor. But sometimes a project can be “aggressively open source,” as <a href="https://datasette.readthedocs.io/en/stable/" class="ext-link" rel="external ">Datasette</a> founder <a href="https://twitter.com/simonw" class="ext-link" rel="external ">Simon Willison</a> puts it, for a bunch of “very selfish reasons.” Willison has written a great deal of open source (e.g., co-creator of <a href="https://www.djangoproject.com/" class="ext-link" rel="external ">Django</a>) and proprietary (e.g., co-founder of <a href="https://en.wikipedia.org/wiki/Lanyrd" class="ext-link" rel="external ">Lanyrd</a>) software in his career, but says he turns to open source as a “creative outlet” that lets him retain a measure of independence, even as full-time employment may constrain it.</p>
  69. <p>You heard that right: Open source doesn’t always need to save the world. It’s enough if it saves an individual developer’s sanity.</p>
  70. <p>Of course, many others do benefit from Willison’s open source contributions. But even if they didn’t, Willison would continue to open source his code. Why? Because it pushes him to write better software even as it helps him keep current with the wide variety of projects — <a href="https://github.com/simonw" class="ext-link" rel="external ">285 repositories and counting </a>— to which he contributes. It’s an incredibly <a href="https://twitter.com/simonw/status/1259180306038308865" class="ext-link" rel="external ">efficient</a> way to build. Willison says that something he enjoys about open source is that for every problem he solves, he’ll never have to solve it again. “The code will be out there forever,” he says.</p>
  71. <p>Indeed, Willison’s approach to open source offers deep insights into how to set up a project for maximum community benefit — even when it’s a community of one.</p>
  72. <h2>Of Showers and Steam</h2>
  73. <p>As Willison explained in an interview, his need for Datasette, a tool for exploring and publishing data, was clear to him in 2009 while working at The Guardian, a UK newspaper. There he helped start <a href="https://www.theguardian.com/news/datablog+technology/data-visualisation" class="ext-link" rel="external ">the data blog</a>, a place to publish the data behind The Guardian’s stories. Though Willison ended up using Google Sheets as a way to publish the underlying static data sets, he longed for a better way to publish and query that data.</p>
  74. <p>Years later, he had his Datasette “shower moment.”</p>
  75. <p>“It was literally a moment and I was literally in the shower,” he says.</p>
  76. <p>First, he reasoned, cloud providers were making hosting dynamic code cheap and easy. Second, he could insert <a href="https://www.sqlite.org/index.html" class="ext-link" rel="external ">SQLite</a>, a widely used, public domain, relational database, into places normally uninhabited by databases.</p>
  77. <p>“If I get SQLite, then export the data, and I bundle it in with a Docker container with a little app that can give you an interface and stuff … Maybe that’s a really interesting space to be exploring,” he says.</p>
  78. <p>And it was. Datasette was born.</p>
  79. <p>But Datasette wasn’t merely a matter of tackling a technical problem. It was also a way for Willison to express himself and to maintain independence. Throughout his career, Willison had found himself within larger organizations. Such employment brings privilege, but it also imposes a restraint on a developer’s ability to build. He says part of his reason for starting Datasette was to have a creative outlet.</p>
  80. <p>“It was an opportunity for me to get really deep into technology again,” he explains. “I wanted a project where I got to decide what to build and have that as my own personal place. It was almost a way of blowing off steam.”</p>
  81. <h2>Open Source Maintenance Group with a Population of One</h2>
  82. <p>Today Datasette has roughly 30 contributors, but most of the work is done by Willison. He’s OK with that. In fact, it’s great, he says, because outside contributions aren’t an unalloyed good.</p>
  83. <p>“There’s a dream that you wake up one morning and there’s a beautiful, shining pull request with a new feature, but actually, if that happens, it’s kind of stressful,” Willison says. “Because you then have to go through the code and review it to make sure it fits the wider model of what you’re trying to build.”</p>
  84. <p>One alternative, he says, is a plug-in architecture, which he incorporated into Datasette almost from the start.</p>
  85. <p>“The beautiful thing about plugins is that I don’t have to give anyone permission to load your plugin, and they don’t even have to talk to me,” Willison explains. He says he could wake up one morning, and Datasette will have a brand new feature, without him doing anything. “And it doesn’t cause any harm to the core if it’s low quality or if it doesn’t quite work,” he says.</p>
  86. <p>Not only does this plug-in architecture protect the Datasette core from <em>others’</em> bad code, but it also protects the core from <em>Willison’s</em> bad code. “I have a lot of crazy ideas, and I don’t want to put those into the core because maybe they’re terrible ideas, but there’s no harm at all in me putting them into a plugin,” he says.</p>
  87. <p>The dream is to become a bit like WordPress, whose plug-in architecture he modeled for Datasette. “WordPress is a very decent CMS with 7,000 plugins that mean it can do anything. I want Datasette to be a ‘decent sort of engine’ for data analysis and exploration with 7,000 plugins that mean that if you want a map or chart or all of these different things, it’s all available to you,” Willison says.</p>
  88. <h2>Open Source for ‘Very Selfish Reasons’</h2>
  89. <p>If Willison’s plug-in approach protects himself and others from bad code, his embrace of open source is perhaps the best way he’s found to ensure he writes good code in the first place.</p>
  90. <p>“Datasette is aggressively open source for a bunch of reasons,” Willison says. “Most of them are very selfish reasons.”</p>
  91. <p>For starters, he explains, he’s written a lot of closed source in his career, including while at Lanyrd, which he and his co-founder/wife Natalie Downe <a href="https://blog.natbat.net/post/61658401806/lanyrd-from-idea-to-exit-the-story-of-our" class="ext-link" rel="external ">sold to Eventbrite</a>. Once you leave that employer, however, you don’t get to use that software again in the future, Willison says.</p>
  92. <p>He points out that even if you’re writing software on your own and not for your employer, if it’s not open source you don’t really build it with a mind to having other people use it, which means it can go stale. When working in the open, by contrast, Willison says, “It forces me to write good code. It forces me to write really good documentation.” Not to mention great unit tests.</p>
  93. <p>That documentation, including associated comments, helps to tell the story of his code. While that may prove useful for outside contributors, docs also helps Willison to pick up where he leaves off. Willison maintains 73 open source projects, and he says the only way you can maintain 73 projects is if you treat every single one of them as if you’re not a core maintainer. Each must have a ReadMe and tests and detailed issue threads discussing what he was working on. “Because then you can drop back into them after a six-month gap and be productive with them,” he says.</p>
  94. <p>“I’m an open source maintenance group with a population of one,” Willison says. “I’m taking the lessons I learned [at Eventbrite], a 600-engineer organization, and applying them to a one-engineer organization.”</p>
  95. <p>A moment of truth now looms for Willison. For the past year, he’s been a <a href="https://jsk.stanford.edu/" class="ext-link" rel="external ">John S. Knight Journalism Fellow</a> at Stanford University, getting paid to tinker with his Datasette dream. His current thought is to do freelance development using Datasette to solve interesting problems related to data journalism for a variety of organizations, which he says could be a great way to figure out what the software should do.</p>
  96. <p>Are you interested in helping Willison shape the future of data journalism? Visit the <a href="https://datasette.readthedocs.io/en/stable/" class="ext-link" rel="external ">Datasette page</a> to contribute code, thoughts, documentation, and more.</p>
  97. </main>
  98. </article>
  99. <hr>
  100. <footer>
  101. <p>
  102. <a href="/david/" title="Aller à l’accueil">🏠</a> •
  103. <a href="/david/log/" title="Accès au flux RSS">🤖</a> •
  104. <a href="http://larlet.com" title="Go to my English profile" data-instant>🇨🇦</a> •
  105. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">📮</a> •
  106. <abbr title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340">🧚</abbr>
  107. </p>
  108. <template id="theme-selector">
  109. <form>
  110. <fieldset>
  111. <legend>Thème</legend>
  112. <label>
  113. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  114. </label>
  115. <label>
  116. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  117. </label>
  118. <label>
  119. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  120. </label>
  121. </fieldset>
  122. </form>
  123. </template>
  124. </footer>
  125. <script type="text/javascript">
  126. function loadThemeForm(templateName) {
  127. const themeSelectorTemplate = document.querySelector(templateName)
  128. const form = themeSelectorTemplate.content.firstElementChild
  129. themeSelectorTemplate.replaceWith(form)
  130. form.addEventListener('change', (e) => {
  131. const chosenColorScheme = e.target.value
  132. localStorage.setItem('theme', chosenColorScheme)
  133. toggleTheme(chosenColorScheme)
  134. })
  135. const selectedTheme = localStorage.getItem('theme')
  136. if (selectedTheme && selectedTheme !== 'undefined') {
  137. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  138. }
  139. }
  140. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  141. window.addEventListener('load', () => {
  142. let hasDarkRules = false
  143. for (const styleSheet of Array.from(document.styleSheets)) {
  144. let mediaRules = []
  145. for (const cssRule of styleSheet.cssRules) {
  146. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  147. continue
  148. }
  149. // WARNING: Safari does not have/supports `conditionText`.
  150. if (cssRule.conditionText) {
  151. if (cssRule.conditionText !== prefersColorSchemeDark) {
  152. continue
  153. }
  154. } else {
  155. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  156. continue
  157. }
  158. }
  159. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  160. }
  161. // WARNING: do not try to insert a Rule to a styleSheet you are
  162. // currently iterating on, otherwise the browser will be stuck
  163. // in a infinite loop…
  164. for (const mediaRule of mediaRules) {
  165. styleSheet.insertRule(mediaRule.cssText)
  166. hasDarkRules = true
  167. }
  168. }
  169. if (hasDarkRules) {
  170. loadThemeForm('#theme-selector')
  171. }
  172. })
  173. </script>
  174. </body>
  175. </html>