1. 3

    The three things that always bothered me about my 17-inch MacBook Pro and why I switched back to Windows are:

    • No longer a 17-inch screen version
    • Weird AZERTY keyboard layout (there’s no key for []{} and I needed to press and memorise 3-keys shortcuts like Alt+Ctrl+5 for each of these characters).
    • Cmd and Ctrl key roles are swapped which made it annoying when working on a Linux or Windows VM at the same time.

    Other than that I loved the POSIX environment and terminal emulator but these issues bother me too much.

    1. 1

      Installation is a bit too complex for a minimal blog generator. Maybe create a Homebrew formula for it?

      1. 3

        While I’d argue it isn’t that complex, you’re generally right – I plan to automate the installation and customization process.

        1. 2

          nix / nixos could probably solve this problem quite nicely.

        1. 20

          “That’s right. A web server. Your CPU has a secret web server that you are not allowed to access, and, apparently, Intel does not want you to know about.” Rejoice!

          1. 1

            The letter from Andrew S. Tanenbaum is interesting too:

            Apparently an older version of MINIX was used. Older versions were primarily for education and newer ones were for high availability. Military-grade security was never a goal.

          1. 1

            Anyone has a link to the commit or initial bug report/fix?

            1. 1

              The bug report (which will probably have the patch, too) is still locked, and the details of the exploit aren’t public yet. My guess is that it’s related to this:

              Devices with the Play Store, as well as AOpen Chromebase Commercial and AOpen Chromebox Commercial will be rolling out over the next few days.

              They probably don’t want to release details until everything is patched.

            1. 3

              I use Firefox on desktop and mobile, but I found that it’s often necessary to switch back to Chrome for some websites, which are either way too slow or plain broken.

              This is a pity, more and more developers target Chrome only, do all the optimisations for it, and kind of assume it will work on other browsers. Chrome is basically the new IE and Google knows that well. Now they can ship whatever change they want that will optimise google.com and YouTube and too bad if the rest of the web and other browsers are broken as a result.

              1. 1

                It seems to be aimed at a specific project, but which one?

                I don’t think what he describes is a general attitude in open source. In my experience, the biggest issue in small projects is that many, even very popular ones, are pretty much unmaintained or abandoned with nobody with a permission to merge PR.

                I’ve never seen a project that refuses PR due to an LTS policy. Some however don’t want to merge because it breaks backward compatibility, which maybe is what this article is talking about. But it’s generally a good thing to have someone to say “no” to a change if it’s going to break the build of dozens of users.

                1. 3

                  On my spare time I continue working on my note taking app Joplin. Having a try with an Electron client for Windows at the moment. This framework is new to me but I’m impressed how easy is to get things running on it.

                  1. 5

                    This looks pretty neat. Interesting that they chose node.js to write it in. I can see the sync with Onedrive and various other services being super useful for some people.

                    I’ve used Notational Velocity a lot in the past. These days I just use Markdown and The Silver Searcher :)

                    1. 3

                      Thanks, actually I’ve started with the Android app in React Native, and then I figured I could re-use most of this code to create a desktop client. There are some drawbacks working with JavaScript but it definitely makes it easier to write cross-platform code. Silver Searcher with markdown files seems like an interesting custom solution too!

                      1. 3

                        I was pretty un-interested in this (after all, vim and grep is my text-based TODO management system), but when I kept reading and discovered you had a console-based app syncing with a mobile client, it really got my interest. Nice work! Frankly, I still see a lot of jank in the stack you’ve chosen, but I won’t bother with criticism. Its just an awesome app. :)

                        However, I must now attest to wondering what better technology to accomplish a ncurses->objcMsgSend nirvana?

                        Would you do another app with this, now you’ve done one?

                        1. 2

                          Thanks, I realise it’s not the most popular stack though in this case it got the job done :)

                          I’m not familiar with what ncurses->objcMsgSend is? Is that a macOS thing?

                          1. 1

                            ncurses on the console (terminal), objcMsgSend on the iOS side of things. Its just a euphemism for what you’ve done .. albeit not a very accurate one. :)

                    1. 2

                      Pity this doesn’t come with an emacs mode. It could leverage a lot of org-mode to do the actual note-side magic …

                      1. 3

                        Have you tried deft? It is one of my favorite Emacs packages, and I use every opportunity I find to shill it.

                        1. 1

                          I have, but I’ve not gotten a chance to dive very deeply into it. You’ve inspired me to take another look.

                        2. 1

                          I’m curious, what emacs features would be useful in this app? It’s possible to change the text editor in which the notes are opened, so it can be set to emacs too, but I guess it’s not what you mean?

                          1. 3

                            Org-mode has todo functionality, outlines/hierarchical lists &c. — you could use what it offers rather than having to reimplement for yourself. It’s basically a Markdown alternative with intelligence added.

                            Writing Joplin as an application within emacs (leveraging org-mode) would also free you from a lot of the nitty-gritty details of dealing with terminals, redisplay, panes/windows, writing a command mode &c. It’s pretty cool (and the reason that I’m more than a bit of an emacs fanatic). Basically, you could stand on the shoulders of others, which is always awesome.

                            Certainly, what you’ve already built is pretty cool. One advantage of writing it in JavaScript — as you have — is that you can easily share the backend between your mobile app and your CLI program. There’s no good story for doing that with elisp right now (or, probably, ever).

                            1. 1

                              On the other hand, you have to embrace Emacs; and for many, that’s a hard sell. Elisp hasn’t been very good performance wise for years, the UI can be problematic even if it’s completely reconfigurable, and Emacs is basically a silo, as you’ve implied. I myself have tried to make peace with Emacs, but it never clicks for me.

                              1. 2

                                Technically you don’t have to write too much elisp. Emacs now supports shared module libraries. I’ve written emacs stuff in go.

                            2. 2
                          1. 2

                            This looks pretty nice. Node.js has surprising good tooling for command line tools.

                            There is a similar set of projects for Ruby called TTY, which I’ve used a fair bit in the past.

                            1. 2

                              Yes there’s quite a few good ones. However, I’ve been going through most Node.js terminal lib recently, and one issue I’ve noticed is that they often are one-man projects, and end up being unmaintained. For instance ncurses, blessed and vorpal, are all pretty much discontinued despite being large and having a lot of issues/pull requests.

                              Terminal Kit is still active after 8 years and the author responsive, which is great, and it also has a very good documentation and tutorials.

                            1. 10

                              This is a nice summary for someone who might have written JS a decade ago. It hits the main points in the field of ‘weird things you have to do to write JavaScript these days’. But there’s one major omission - package managers. I still have a hard time explaining to myself why we have bower, npm, and yarn all solving the same problem with varying degrees of success.

                              1. 11

                                Bower is officially deprecated. Yarn was created to solve problems that existed in npm < 5 [1]. With the release of npm 5+, you can ignore package managers other than npm at this point.

                                [1] Exactly reproducible dependency trees & performance being the main ones.

                                1. 1

                                  [1] Exactly reproducible dependency trees

                                  Did they remove the ability to re-publish a given version of a package? Hopefully - if not, this feature is just a cake-lie.

                                    1. 1

                                      Great! Do you have more info on the “reproducible” stuff? I am not finding mention of it.

                                      1. 1

                                        From the npm 5 release announcement:

                                        npm will --save by default now. Additionally, package-lock.json will be automatically created unless an npm-shrinkwrap.json exists. (#15666)

                                        This package-lock.json is what provides reproducible dependency trees. It’s the equivalent of Yarn’s yarn.lock.

                                        1. 3

                                          Well. I guess I have a different meaning of reproducible.. Previous versions of NPM would have variance in things like Makefiles that gyp puked out.. this meant that something that was “npm shrinkwrap’d” and then tar’d up, would be different every time. This is the reproducability I was hoping for.. guessing the lock file doesn’t give me that.. But I will play around and see.

                                          1. 2

                                            Not sure if it’s possible to enforce that. You would need to ensure that any postinstall hooks are deterministic, so no calls to Math.random() or new Date().

                                            You also would have to forbid making network requests, accessing the filesystem and really anything that leaves pure javascript land.

                                            1. 1

                                              Yeah. No idea about that aspect. :-/

                                    2. 1

                                      It still seems like yarn is faster, according to a few blog posts. The margin is much closer, however.

                                      1. 1

                                        If nothing else, it’s much more consistent. At my company I’ve been migrating our apps from using bower and npm packages to exclusively npm packages, and setting our build servers to use yarn instead of npm. It solved our issue with npm intermittently crashing our builds.

                                      2. 1

                                        I use yarn because it’s way faster than npm, doesn’t even compare. I develop under WSL though so maybe the difference is more noticeable there.

                                        1. 2

                                          I use yarn because it’s way faster than npm

                                          This has not been my personal experience. npm 5 has been consistently as fast or faster than yarn in my testing on Windows 10. YMMV.

                                          1. 1

                                            I’ve just tried deleting the node_modules directory in my current project with 780 packages and ran npm install- it took 53s. Then I’ve done this again and ran yarn install and it finished in 27s. It’s possible it’s something specific to my project or to the fact that I’m running under WSL (maybe npm hits some under-optimised part of it), but anyway in my particular case it’s indeed faster with yarn.

                                    1. 26

                                      exa is written in Rust, so it’s small, fast, and portable.

                                      -rwxr-xr-x  1 root      wheel    38K 28 Apr 20:31 /bin/ls
                                      -rwxr-xr-x@ 1 curtis    staff   1.3M  7 Jul 12:25 exa-macos-x86_64
                                      

                                      ?

                                      1. 9

                                        Stripping it helps a bit… but not much though.

                                        $ du -hs exa-macos-x86_64  
                                        1.3M	exa-macos-x86_64
                                        $ strip exa-macos-x86_64     
                                        $ du -hs exa-macos-x86_64  
                                        956K	exa-macos-x86_64
                                        

                                        More fun is what it links to:

                                        $ otool -L /bin/ls            
                                        /bin/ls:
                                        	/usr/lib/libutil.dylib (compatibility version 1.0.0, current version 1.0.0)
                                        	/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
                                        	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.60.2)
                                        $ du -hs /usr/lib/libutil.dylib /usr/lib/libncurses.5.4.dylib /usr/lib/libSystem.B.dylib
                                         28K	/usr/lib/libutil.dylib
                                        284K	/usr/lib/libncurses.5.4.dylib
                                         12K	/usr/lib/libSystem.B.dylib
                                        $ otool -L /tmp/exa-macos-x86_64
                                        /tmp/exa-macos-x86_64:
                                        	/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
                                        	/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 57740.60.18)
                                        	/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1349.8.0)
                                        	/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.8)
                                        	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.60.2)
                                        $ du -hs /usr/lib/libiconv.2.dylib /System/Library/Frameworks/Security.framework/Versions/A/Security /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation /usr/lib/libz.1.dylib /usr/lib/libSystem.B.dylib
                                        1.6M	/usr/lib/libiconv.2.dylib
                                        9.3M	/System/Library/Frameworks/Security.framework/Versions/A/Security
                                        9.7M	/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
                                         96K	/usr/lib/libz.1.dylib
                                         12K	/usr/lib/libSystem.B.dylib
                                        
                                        1. 6

                                          To be fair, exa is a self-contained executable, while ls probably has a dependency to libc, which it loads dynamically. If Rust ever becomes very popular and its runtime is installed by default everywhere, its executables will also be a few KB only.

                                          1. 4

                                            FWIW, linking ls from GNU coreutils statically with musl-libc on x86_64 gave me a 147K ELF with no shared object dependencies.

                                            1. 3

                                              For that to be true Rust would have to have well defined and stable ABI. Which it doesn’t have right now.

                                              1. 3

                                                Rust binaries actually do dynamically link to libc. Its standard library, which calls libc, is statically compiled into binaries.

                                            1. 2

                                              it’s still no good if you use firefox to access gmail, google search, and other websites with google tracking code in them.

                                              1. 5

                                                Recaptcha is the most insidious of those since it’s unavoidable if you want to use whatever site is using it. uBlock can disable webfonts and block adsense and analytics, Privacy Badger replaces share buttons with local copies, and Decentraleyes does the same for javascript libraries.

                                                e: oops, double-posted

                                                1. 9

                                                  On a tangential remark, did anyone notice how Google took Recaptcha and re-purposed the idea by transitioning from deciphering words to helping Google Street View with corrections? I remember I used to have to decipher some obscure text back in ‘09, before Google bought Recaptcha. Now I have to choose “store fronts” from a bunch of photos, or identify which fixed width x height segments of a larger photo contain a street sign. We'VR become free workers to their product, and that really rubs me in the wrong way.

                                                  1. 2

                                                    I do most of this. My point is that the author seems to be implying using firefox will help stop google’s hold over the entire web, when it really will not. You need to be careful, avoid certain services and we be willing to live with the inconvenience. :-(

                                                    reCAPTCHA is driving me nuts though. Does anyone know of an alternative I could deploy which doesn’t depend on old php code?

                                                    1. 1

                                                      What are you trying to achieve with reCAPTCHA - prove the visitor is not a bot? Stop spamming?

                                                      1. 5

                                                        Both of those things, yes

                                                        1. 1

                                                          The latest version of reCaptcha is mostly JavaScript code with a thin server-side script for validation (which can easily be implemented in any language).

                                                          1. 3

                                                            You’d still have to connect to Google to receive the challenge (and you’d still be helping to build their AI)

                                                    1. 6

                                                      10% ain’t worth having my app made legacy with two weeks notice.

                                                      1. 8

                                                        0.16 -> 0.17 was a big change but I’m puzzled by the implied criticism. Does version 0.16/0.17 indicate “ready for production”? I always inferred from the versions and the sparse documentation & libraries that Elm is experimental, so I’m very cautious about using it for any production stuff (and so I wasn’t bothered by the 0.16 -> 0.17 transition). Projects in experimental phase can and should change, wouldn’t you say?

                                                        Unfortunately, these days nobody seems to indicate clearly (or even to know?) whether the project is stable and suitable for production use, and that’s a problem, but it’s definitely not unique to Elm.

                                                        1. 5

                                                          React was at 0.14 but in production on things like Instagram for a while. I think Elm is decently ready for production, but there is the whole Clojure-style “at the whims of the BDFL” vibe going on, esp. with the subscriptions change.

                                                          That being said, I think it’s mostly fine. Types make this a lot less painful than (say) Py2 -> Py3. If you really like Elm, this is a small cost to pay compared to the pain of using a pure-JS (or even Typescript) environment. In the worst case, it’s a vibrant experiment in building front-end apps. The tools and practices that are showing up are more important than the language IMO.

                                                          There is also the whole thing where you can stick on older versions for a while if you don’t want to do the change just yet. It’s not like older stuff disappeared. Py2/Py3 sucks but if you were on Python 2, you were mostly fine.

                                                          I think the more frustrating thing is that there wasn’t a clear deprecation path. Ideally, you have one or two versions where both mechanisms are in place, so you can gradually port things .

                                                          Though I say this as a Purescript user who has all dependencies break at every minor version release ;)

                                                          1. 1

                                                            You do that much triangulation and marketing about being the “practical” alternative, some expectations are implicit to that.

                                                            1. 1

                                                              Fair enough. I guess I just don’t believe any tech marketing after having a sufficient number of painful lessons. My own experiments showed that Elm is still quite a long way off from being production ready, and I treat it accordingly.

                                                              1. 1

                                                                shrugs We use GHCJS in prod. It may be even less mature than Elm or PureScript, but it doesn’t dictate to us how to build our app, we share datatypes with the backend, and the compiler is very hackable. The GHCJS creator is very easy-going and modest as well.

                                                          2. 4

                                                            Signal noted.

                                                            (Sorry. Bad pun.)

                                                            1. 2

                                                              Yep and I wouldn’t want to be the guy who has to debug this once the lib is deprecated (code generated from the simple “form” example):

                                                              var _user$project$Temp1472650027426490$viewValidation = function (model) {
                                                                  var _p0 = _elm_lang$core$Native_Utils.eq(model.password, model.passwordAgain) ? {ctor: '_Tuple2', _0: 'green', _1: 'OK'} : {ctor: '_Tuple2', _0: 'red', _1: 'Passwords do not match!'};
                                                                  var color = _p0._0;
                                                                  var message = _p0._1;
                                                                  return A2(
                                                                      _elm_lang$html$Html$div,
                                                                      _elm_lang$core$Native_List.fromArray(
                                                                          [
                                                                              _elm_lang$html$Html_Attributes$style(
                                                                              _elm_lang$core$Native_List.fromArray(
                                                                                  [
                                                                                      {ctor: '_Tuple2', _0: 'color', _1: color}
                                                                                  ]))
                                                                          ]),
                                                                      _elm_lang$core$Native_List.fromArray(
                                                                          [
                                                                              _elm_lang$html$Html$text(message)
                                                                          ]));
                                                              };
                                                              var _user$project$Temp1472650027426490$update = F2(
                                                                  function (msg, model) {
                                                                      var _p1 = msg;
                                                                      switch (_p1.ctor) {
                                                                          case 'Name':
                                                                              return _elm_lang$core$Native_Utils.update(
                                                                                  model,
                                                                                  {name: _p1._0});
                                                                          case 'Password':
                                                                              return _elm_lang$core$Native_Utils.update(
                                                                                  model,
                                                                                  {password: _p1._0});
                                                                          default:
                                                                              return _elm_lang$core$Native_Utils.update(
                                                                                  model,
                                                                                  {passwordAgain: _p1._0});
                                                                      }
                                                                  });
                                                              
                                                            1. 3

                                                              Antivirus processes are often unkillable, even as admin, so there must be a way although I guess it’s not pretty.

                                                              1. 9

                                                                What a terribly worded dialog box, I’ve stayed there reading it for a while and I’ve decided that “Remove download” is what would remove the song from iCloud but keep it on the hard drive. However, according to the article, this option would delete it from the drive and leave it on iCloud. So I guess “Delete Song” is the right button if you want to keep the song. How stupidly confusing.

                                                                1. 6

                                                                  I’ve turned off iCloud on all of my apple devices because all of the messages around it are very unclear. It’s hard to tell what’s being deleted or moved and exactly where files are going.

                                                                1. 17

                                                                  They are very transparent about what they do in this document, which is good, however silently enabling this (even with opt-out) is a very poor move that’s going to annoy a lot of users. They should at least have prompted the user on installation, if only to notify them that this feature is now enabled.

                                                                  1. 9

                                                                    Yeah, I agree. If the question was posed to me during first interactive usage after an upgrade, I honestly would have probably said no, but I would have at least considered it and followed any links and read the reasoning behind it. I certainly wouldn’t have been upset or anything, and would have appreciated the openness of information in that document. Probably even ending up more positive/confident about homebrew in general.

                                                                    As it stands now, I apparently have been sending google data completely unbeknownst to me for some period of time. So instead, I am now left with a “wow, not cool!” kind of reactionary feeling. Some amount of trust has indeed been lost – next time I hear about a new package manager for osx (still need to check out Nix) I will think “oh! I should check this out” instead of “meh. homebrew still works fine”.

                                                                    1. 2

                                                                      From the article:

                                                                      You will be notified the first time you run brew update or install Homebrew.

                                                                      Is that not true?

                                                                      1. 1

                                                                        I didn’t see the update message, I’m sure it’s there somewhere but I didn’t notice it anyway as a data-point of 1.

                                                                        1. 1

                                                                          That notification was added later (approx 10 hours after I posted the thread). Initially this functionality arrived with no notification.

                                                                          1. 1

                                                                            Damn git!

                                                                            1. 1

                                                                              heh heh. I had actually (briefly) considered linking to the version/sha at the time I posted it, but that seemed excessive. In hindsight…maybe not!

                                                                      1. 4

                                                                        Short identifiers: A lot of Go code, including the snippets in the documentation, is plagued by extremely short identifiers. Those are used as local variables, function arguments, method receivers, and even structure members. Combined with the heavy use of interfaces, it is hard to read the code: What’s a c? And an r? And an s.wg? Yes, there is bad code all around, but when the official language documentation seems to recommend this approach, you can expect that others will follow the apparent recommendation in all cases.

                                                                        I also don’t get their obsession with using very short names. Also the use of very common short names for the default packages is annoying as it pollutes the namespace. Things like “user”, “url”, “bytes” are package names, which means they can’t be used as variable names. So instead of writing something clean like user := user.Current() one has to write something like u := user.Current(). I know it’s possible to use aliases for the package names but it’s not an ideal solution. By default, common names shouldn’t be reserved like that.

                                                                        1. 3

                                                                          Also the use of very common short names for the default packages is annoying as it pollutes the namespace. Things like “user”, “url”, “bytes” are package names, which means they can’t be used as variable names.

                                                                          IMO “user” is a bad variable name - “current_logged_in_user” or “authenticated_user” sure. the only time I think user is good as a variable on its own would be when you’re doing a loop over a list of users, or working in a tightly scoped function, and in either of those cases u seems just as good to me.

                                                                          But I think my fundamental objection to your argument: what should the package that works with bytes be called if not bytes?!

                                                                        1. 12

                                                                          It’s 2016 and Node’s developers haven’t figured out yet that global names are never a good idea. Using UUIDs or as the article suggest namespaces would be a straightforward solution, but it’s amazing that they didn’t do that from the start.

                                                                          1. 4

                                                                            What exactly would namespacing have solved in this instance?

                                                                            Let’s review what happened (within the package repo):

                                                                            • current version of left-pad is unpublished
                                                                            • npm decides break their own rules to avoid breakage: re-publishes old version (normally impossible) under new author

                                                                            If this scenario happened with namespaced package, what would stop npm from breaking their own rules again, and transfer ownership of the (now namespaced) package?

                                                                            1. 6

                                                                              Namespacing would prevent someone from republishing a malicious new version to anyone who uses caret dependencies.

                                                                              Hell, namespacing could have prevented this entire fiasco since kik would really only need ownership over the “kik” namespace, not every package named kik.

                                                                              1. 6

                                                                                Namespacing would prevent someone from republishing a malicious new version to anyone who uses caret dependencies.

                                                                                Again, if I unpublish a package from my namespace, and npm decides to break the rules to avoid massive breakage – what has changed?

                                                                                Also keep in mind that I’m not arguing against namespacing in general, but I don’t think it would’ve helped with anything in this case.

                                                                                Hell, namespacing could have prevented this entire fiasco since kik would really only need ownership over the “kik” namespace, not every package named kik.

                                                                                IANAL but that seems speculative to me.

                                                                                EDIT: See this DMCA for GitHub, which does have namespaces: https://github.com/github/dmca/blob/master/2014-02-12-WhatsApp.md

                                                                                1. 4

                                                                                  No one can stop npm from doing whatever, but namespacing done by npm username would prevent a malicious third-party from uploading a new version of the package. I’m saying that is an improvement, not that it would have changed anything.

                                                                                  And yes, kik could still send a DMCA, but they would have much less reason for doing so. In this case, they probably wanted to publish a module for accessing their API and found that the logical name was already in use.

                                                                                  1. 1

                                                                                    namespacing done by npm username would prevent a malicious third-party from uploading a new version of the package

                                                                                    Something that hasn’t happened, and as far as I can see doesn’t happen. ~/^ dependencies are always a tradeoff and it’s not clear to me that upgrading to newer versions after a package was handed over to a third party isn’t the intended behaviour in that case.

                                                                            2. 2

                                                                              It’s 2016 and people are still using NPM as a build tool even after being explicitly told not to deserve their builds breaking because of political shit like this.

                                                                            1. 9

                                                                              I wouldn’t write off desktop mail clients just yet. Both Pine (without Maildir patch) and Eudora use the mbox format, which is prone to corruption as it is essentially all of your messages concatenated in one plaintext file. For large volumes of email, it’s better to use the Maildir format, which stores each message in a separate file.

                                                                              1. 6

                                                                                Maildir is certainly better than mbox (which is an abomination), but it is still susceptible to the problems outlined in the OP.

                                                                                1. 1

                                                                                  Doesn’t thunderbird store messages in a SQLite DB?

                                                                                  1. 1

                                                                                    I dunno. My thunderbird corrupted (again) recently and i gave up.

                                                                                    1. 1

                                                                                      It uses the mbox format too. I wonder indeed why they don’t use something more robust like SQLite.