1. 4

    I still strongly agrees with this XKCD strip: https://xkcd.com/2030/

    I write software for 15 years, and I firmly believe that paper ballots, put in transparents boxes, counted on the place it was cast, is much easier to secure, inspect, and gives much more confidence to voters than any (centralized or not) digital voting system :x

    1. 3

      Isn’t that just what any reverse proxy, like Varnish does? Along with comprehensive cache header & invalidation? Am I missing something?

      1. 4

        The draw is that it works with single page applications.

        Varnish would cache your empty template, then the browser would load in all the JS & render the site. From what I understand of next.js, your single page app is essentially pre-rendered & cached server side so the client gets content immediately, then your SPA takes over from there.

        1. 2

          Yeah, that’s a good way of putting it. I suppose with static generation we can deploy a warmed cache version of our site without the server having to do the initial render.

          It’s the “little things” I suppose :)

        2. 0

          In effect, yes.

          Perhaps the “point of innovation” here however is that this new feature promotes a “static-first” approach to building Next apps. In effect, our entire app could just be some static HTML/CSS/JS files. The server (or serverless functions) only come into play when a page needs to be regenerated (or cache invalidated).

          Same results but much easier implementation. Varnish setup is a pain.

        1. 4

          Cloudflare is shielding cybercriminals

          It’s true Cloudflare protect victims from DDoS, but also protect attackers. Although, if there are no DDoS attacks, whom will Cloudflare protect you from? It’s basically money making machine. Both attackers and legit endusers use it for protection. Vice versa profitable.

          1. 4

            This troubled me, as there is no source for that. Only a statement from the website’s owner :/

            1. 4

              It’s true though.

              Cloudflare serves all customers willing to pay, and even has a free tier for some products. It doesn’t generally vet customers. You don’t have to prove that you’re worthy of using whatever Cloudflare product you wish to pay for, and Cloudflare protects its customers. That includes criminals. Most notably, it includes shielding criminals from people who send email saying “FOO BAR IS A CRIMINAL! I’M TELLING YOU FOO BAR IS A CRIMINAL!”

              Amazon does the same — anyone can buy things there and the selection includes many useful tools, so Amazon sells burglary tools to criminals. But it’s not a universal standard. IIRC all of the big British banks eventually caved in to public pressure and closed the accounts of some customers the vegans didn’t like.

          1. 11

            I work for an ecommerce company, and I can tell you terminology matters, when business process are complicated.

            Simple example : “price”. Possibilities :

            • The price we bought an item ?
            • The price we sell it ?
            • The manufacturer’s suggested retail price ?
            • The price we sell it, after promotions ?
            • The price a customer may buy it with some vouchers ?
            • The price a customer may buy it with the loyalty program’s special day activated ?

            Using actual business wording allows to evade so many bugs, it really damages the code quality to stick with what OP calls industry-standards name in the codebase, if the business have something that is both easier and more precise to use.

            1. 6

              Oh god this. Had that delightful experience for “cost”:

              • Cost to us the market maker?
              • Cost to the producer?
              • Cost to the buyer?
              • Actual cost, or the estimated cost, for any of the above?
              • Base cost or cost plus various markups, for any of the above?

              It was so bad.

              1. 2

                Most code bases I’ve seen in Germany are still 100% English, this usually makes total sense.

                A former employer of mine was writing software for a bank and it was a bit of an odd mixture of getX and calculateY with X and Y and so on being technical financial math terms, all in German - and it actually was the best possible solution. No one (not even the domain experts) would have known how to translate the stuff to English because it’s mostly terms no one has ever heard or uses in their daily life. The alternative would have been to also not use the “getX/setX/calculateX/doX” verbs, but it in the name of common practices that was decided against.

            1. 10

              This post is really great, I wish I had those when I began working remotely. It applies to remote jobs, coronavirus or not.

              1. 1

                Gee’, this uncovers quite a lot of stuff I didn’t know, especially this one: Delphi & Pascal are still alive. What a world to live in!

                1. 2

                  The majority of the companies I worked for prefer “entreprise pricing”, because it allows to know exactly how much will be paid, each month/year.

                  Some of accounting services simply refuses to pay for a service they don’t know in advance how much will cost, or to “plug a credit cart on a subscription”.

                  Also, pay-as-you go may end up in a huge bill when volume suddenly increases, due to a bug or some DDOS/scraping. Fixed quotas allow you to get notified before.

                  1. 1

                    @ddevault you should consider hiring an intern

                    1. 2

                      I think @ddevault should consider taking more care of himself, because the way that article is written, I don’t see how sustainable it might be, without taking a toll on his health :(

                    1. 1

                      This looks a lot like the GTD methodology applied. Always nice as a reminder.

                      1. 5

                        Just to see if I understand the setup:

                        If I pester the default public Rendezvous Server with random 16 bit keys I might receive files from people who are sending (but where the receiver isn’t started yet)? Sure, the sender will get to see my IP address, and you can use bigger key sizes or use a private server.

                        1. 3

                          Yes, it’s even stated in the doc https://magic-wormhole.readthedocs.io/en/latest/attacks.html#dos-attack-on-the-rendezvous-server

                          In particular, grumpy people could disrupt service for everyone by writing a program that just keeps connecting to the rendezvous server, pretending to be real clients, and claiming messages meant for legitimate users.


                          The core problem is that, because things are so easy for the legitimate participants, they’re really easy for the attacker too. Short wormhole codes are the easiest to use, but they make it for a trivially predictable channel-id target.

                          1. 3

                            Yes, but critically you wouldn’t be able to decrypt the file, because the PAKE only gives you one chance at guessing the code for a specific peer, so you can only disrupt the transfer.

                            1. 3

                              I think that if you guess one of the currently active default 65k channels, you get the data.

                              Your only factors of authentication are time frame and 16bits of randomness.

                              1. 2

                                Ah I misinterpreted. Yes, ignoring the channel ID, you can keep making guesses, and on average you’ll disrupt 32k transfers before taking one over.

                                It doesn’t change anything to try all 64k combinations, you can just keep trying “foo-bar”, and wait for it to be correct, because you only get one shot at breaking each random transfer.

                          1. 23

                            FTFY: “A plea to developers everywhere: Write Junior Code”

                            Let’s get bogged down with how much simple code we write.

                            God, I wish every developer would make an effort to write simple code.

                            1. 7

                              I don’t disagree with you at all, but Haskell does have a bit of a spiral problem with these sorts of things; often folks writing even simple Haskell programs end up using very exotic types that are abstruse to more junior devs (or even more senior devs who just haven’t looked at, say, lenses before). I have this tweet about a simple dialect of Haskell saved because I think about this often when interacting with Haskell code.

                              1. 8

                                Those exotic types describe complexity that is present in other languages as well. However, in other languages, you do not need the type checker’s permission to introduce complexity. Instead, you discover this complexity after the fact by debugging your program.

                                It is questionable whether the Haskell approach is as wise as it is clever. At least to me, it does not seem very suitable for writing what the original post calls “junior code”. Consider some of Haskell’s main features:

                                • Purity and precise types:

                                  • Benefit: You can use equational reasoning to understand the complexity in your code.
                                  • Drawback: You cannot ignore the complexity in your code, even when it does not matter to you.
                                • Lazy evaluation:

                                  • Benefit: It is easy to write programs that manipulate conceptually large data structures, but in the end only need to inspect a tiny part of them.
                                  • Drawback: It is difficult to track the sequence of states resulting from running your program.
                                • Higher-kinded types:

                                  • Benefit: It possible to abstract not only over concrete types, such as Int or String, but also over “shapes of data types”, such as List or Tree (leaving the element type unspecified).
                                  • Drawback: Oftentimes, type errors will be an unintelligible mess.

                                It is ultimately a subjective matter whether these are good tradeoffs.

                                1. 6

                                  often folks writing even simple Haskell programs end up using very exotic types

                                  … abstruse …


                                2. 1

                                  Isn’t a large aspect of Java and C# that they force you to write simple code? Then they get called “blub” languages or whatever. The reality is that you should write for whoever your audience is. Explaining everything such that a six-year old can understand it requires an inordinate amount of effort and without picking a target audience this is what your suggestion devolves into.

                                  1. 7

                                    Isn’t a large aspect of Java and C# that they force you to write simple code?

                                    No. C# has had type inference, covariant and contravariant generics, opt-in dynamic typing as distinct from type inference, lambdas, value variables, reference variables, checked and unchecked arithmetic, and G–d knows what else I’m forgetting since at least the late 2000s. Java’s missing some of that (although less and less recently), but adds to it things like implicit runtime code generation, autoboxing, and a bunch of other stuff. Neither language is intrinsically simple.

                                    But that said, I don’t honestly know that they’re honestly much more complicated than most languages, either. They’re more complicated than Go, maybe, but I don’t even know for sure if they’re more complicated than Python. The thing is that Java projects—at least, the “enterprise” ones for which the language has become famous—go crazy with complexity, despite—and often at odds with—the underlying language. There’s nothing preventing Python from doing absolutely crazy things, for example, and people who remember pre-1.0 versions of Django might recall when it used metaclasses and what would now be importlib to make one hell of a lot of magic happen in model classes. But the community rejects that approach. The Java community, on the other hand, is happy to go crazy with XML, factories, and custom class loaders to roam way into the Necronomicon of software development. I tend to regard this as the ecosystem, rather than the language, going to the extreme.

                                    Haskell in practice, to me, feels like what C# or Java code taken to the extreme would look like. And there’s even indeed libraries like language-ext for C# or Arrow (which is for Kotlin, but same difference), which do go there, with (IMVHO) disastrous results. (Disclaimer: I work heavily on an Arrow-based code base and am productive in it, albeit in my opinion despite that comment.) This is also an ecosystem decision, and one that I think this article is rightfully and correctly railing against.

                                    1. 5

                                      There’s nothing preventing Python from doing absolutely crazy things, for example, and people who remember pre-1.0 versions of Django might recall when it used metaclasses and what would now be importlib to make one hell of a lot of magic happen in model classes. But the community rejects that approach.

                                      I don’t think that’s true at all. The difference is that Python has good abstractions, so if you want to do something complex under the hood, you can still expose a simple interface. In fact, Python programmers would much rather use something with a simple interface and complex internals than the other way around. That’s why they’re using Python!

                                      1. 4

                                        I’m not sure we’re disagreeing, except for I think you’re implying that Java and C# lack an ability to expose something with complex internals and a simple interface. I’m logging off tech for the weekend, but Javalin is a great example of a Java framework that’s on par with Flask in terms of both simplicity and power, and done with 100% vanilla Java. It’s just not popular. And the reason I cited early versions of Django for Python is specifically because the community felt that that tradeoff of a simple interface for complex internals went too far. (If you have not used way-pre-1.0 versions of Django, it did Rails-style implicit imports and implicit metaclasses. We are not talking about current, or even 1.0, Django here.)

                                        In other words, I think you’re making my point that this is about culture and ecosystem, not language in the abstract. Which is also why this article is making a plea about how to write Haskell, and not about abandoning Haskell for e.g. OCaml.

                                        1. 4

                                          Ah right yes I see about the Django thing. I was thinking about how it uses them now. I wasn’t aware it did import magic before, that definitely sounds a bit much!

                                  2. 1

                                    I used to use juxt and comp and partial quite a bit in my Clojure code, but these days I try to avoid them. They’re clever, they’re fun, they’re succinct… but they can also make it harder for the next person who comes along if they’re not already a Clojure hotshot.

                                    1. 6

                                      That’s setting a pretty low bar, isn’t it? Partially applying functions isn’t exactly whizz-bang fancy-pants programming in a Lisp.

                                      1. 2

                                        And yet, there’s usually another way to write it that’s more clear to someone not as familiar with Lisps.

                                        (I’m not saying “never use these”. There are definitely times when it’s more awkward to use something else.)

                                        1. 3

                                          Function composition is the most fundamental functional programming concept as far as modularity is concerned, and partial application is not far behind. They are not specific to Lisps. juxt is slightly more “clever,” but nonetheless provides a ton of utility, is a part of the core library, and should not be shied away from. Talking about avoiding these functions without explicit examples or clear criteria is pointless.

                                          Do you disapprove of any macro usage in your Clojure code? Are transducers out? What about core.async? I’ve seen more “clever” and confusing code written using those features than with any of the functions you’ve listed. For that matter, the worst (all?) Clojure codebases tend to be agglomerations of layer after layer of “simple” map-processing functions which are impossible to grasp in the aggregate and incredibly frustrating to debug. This is evidence of a general lack of coherent system-level thinking, versus any specific features in Clojure being responsible for complex, unmaintainable code.

                                          The guidelines for writing clean, simple, maintainable code are never so straightforward such that they can be stated pithily, to the chagrin of Rich Hickey true-believers everywhere. It’s a combination of figuring out what works for a given team, adopting conventions and architecture well-suited to the domain, and choosing an environment and libraries to integrate with so that you introduce as little friction as possible (and probably more that I’m forgetting, unrelated to the choice of language). But picking and choosing arbitrary functions to eschew will not get you very close to the goal of writing simple code.

                                          1. 2

                                            I think you’re taking this a lot farther than what I actually said.

                                            1. 2

                                              I’m sorry, I was trying to respond systematically to a comment I disagreed with. If you wouldn’t mind: how exactly did I take it too far?

                                              1. 1

                                                Well, I didn’t say “don’t use these”, I said that I “try to avoid them”. I don’t always succeed in that, and I’m happy to use them where they make sense.

                                                There’s a continuum between “can’t avoid it” and “totally gratuitous” and I try to push my personal cutoff towards the left, there. When it would make the code harder to read, I don’t avoid them!

                                                1. 1

                                                  Well, I didn’t say “don’t use these”, I said that I “try to avoid them”. I don’t always succeed in that, and I’m happy to use them where they make sense.

                                                  Why do you try to avoid using them? When does it make sense to use them?

                                  1. 6

                                    This somehow manages to make IPv6 addresses less ergonomic than they already are.

                                    If you are setting up a prototype to the stage where you need an SSL certificate, surely it’s not much of a stretch to add an AAAA record to a domain name?

                                    If you’re prepared to go down the road of self signed certificates, you can even issue one to your bare IP if you so wish.

                                    1. 3

                                      If you are setting up a prototype to the stage where you need an SSL certificate, surely it’s not much of a stretch to add an AAAA record to a domain name?

                                      In many (large) organisation, this would not be that easy. Domain name entries are usually filtered, and need some kind of validation/process. When building a prototype or a POC for a project, it might be cumbersome.

                                    1. 2

                                      Oh god, I wish there was LICEcap available for linux. I miss it :(

                                      1. 2

                                        Peek is pretty good.

                                        1. 1

                                          It works under wine.

                                        1. 2

                                          Here’s my related question: should a serialization program be non-deterministic?

                                          Because I’m not sure I want to live in a world where json({a:1, b:1}) != json({a:1, b:1})

                                          1. 2

                                            If both serialised forms represent the same data structure, why should that matter? You care about the data, not its representation (unless you’re writing a json library yourself, or in daft situations like the article, of course).

                                            1. 1

                                              Same here. Also, I gave it a thought, and I couldn’t find any value provided by controlling the serialization order :/

                                              1. 1

                                                If your map data structure isn’t already deterministic, then non-deterministic serialisation allows serialising keys out as the map is enumerated, which saves both memory and clock cycles. If you are in right circumstances to be able to use that fact and also in the circumstances to need it, you’d want it.

                                                You certainly don’t want to live in a world where determinism isn’t the default though.

                                                1. 1

                                                  Looking at the problem from a more mathematical perspective, we can see that encode: Hash -> String is not a function as it’s multi-valued. However, decode: String -> Hash is a function, although not injective.

                                                  We can introduce an equivalence relation in String (for simplicity, I’m assuming only valid JSON outputs) such that s1 and s2 are equivalent iff decode(s1) == decode(s2). Then we can fix the problem by either:

                                                  1. Making encode* map to the corresponding equivalence class, i.e. to a set of strings that all decode to the input object.
                                                  2. Picking a canonical representation in each equivalence class.

                                                  Implementation-wise, option 1 would mean defining a type like:

                                                  module JSON
                                                    class EncodedValue
                                                      attr_reader :object
                                                      protected :object
                                                      def initialize(object)
                                                        @object = object
                                                        @result = nil
                                                      def to_s
                                                        @result ||= actually_encode(@object)
                                                      def ==(other)
                                                        other == other.object
                                                      # ...

                                                  Option 2 entails deciding on how to represent values if multiple options are allowed. This includes:

                                                  • Key ordering in objects.
                                                  • Number representation (e.g. 100 and 1e2 are both valid JSON).
                                                  • White space use.
                                                  • Escape sequences in strings.
                                                1. 2

                                                  I don’t know man… It’s already too hard for many humans, including me :x


                                                  1. 2

                                                    I found too that taking these kind of “lab notes” when digging really deep in a weird bug helps a lot :

                                                    • You can go back to what you done previously, compare command outputs
                                                    • You can use that to onboard another brain on it
                                                    • You can use it to rollback and try other “paths”, when one did not work.

                                                    PS: kudos to:

                                                    git clone git@github.com:nodejs/node.git

                                                    it’s getting serious now

                                                    1. 11

                                                      As usual, this misses the one important point of unit tests in languages with few static guarantees: To test that various code paths actually work at all. How to be sure that you don’t get a type mismatch exception in some error case except by faking that error?

                                                      For languages like Go, I tend to agree with the author.

                                                      1. 4

                                                        How to be sure that you don’t get a type mismatch exception in some error case except by faking that error?

                                                        Not to be that guy, but… static type systems sort of get rid of that whole class of issues without the need for tests.

                                                        Writing big mission-critical systems in languages where a single typo or a function signature change can bring down the production system at some random point in the future seems unwise, the kind of unit testing which mostly exists to catch type errors and typos seems like a pretty expensive fix.

                                                        1. 3

                                                          Same point of view. Try ditching all unit tests on a PHP codebase, or a JS one. A strong type system really shines, and often outclass unit tests.

                                                        1. 2

                                                          Good advice on the dates. I prefer my emails to look like a well-keyworded search engine query.


                                                          Dear Phoebe,

                                                          I didn’t understand the process described on the issue about the logging bug. Irida’s comment was not very clear either. Can you please help me?





                                                          I didn’t understand <link to issue>, even with Irida’s comment. <Please, can you help me?>

                                                          Thanks <optional name>

                                                          Your name should be in the email headers.

                                                          1. 3

                                                            In you “after” version, as a reader, I don’t know what issue you are talking about, where on the “before” version, I know it’s about “the issue about the logging bug”.

                                                            I fear that you might end up with many similar emails, where only the link changes :(

                                                            1. 2

                                                              There would be a link to the issue.

                                                              1. 1

                                                                Yes, linking to the issue is good but for this particular type of comm, I prefer tagging people in the comment section of the issue. I’m assuming we are using something like github/gitlab/bitbucket/JIRA issue tracker system where comments are allowed, and you can tag people.

                                                          1. 2

                                                            That is neat, the kind of things you are really thankful to the blog’s author to have written, as it’s a rather obscure edge-case (at least from my point of view).

                                                            For those (like me) wondering why there’s no such mechanism on MySQL : https://stackoverflow.com/questions/25153532/why-is-it-a-vacuum-not-needed-with-mysql-compared-to-the-postgresql

                                                            PS: I’d suggest removing the [ruby] tab, and go with a [postgre] one.

                                                            1. 2

                                                              Just one clarification as PostgreSQL vacuum just does a lot of things and I think this behavior could have also happened with MySQL.

                                                              In this specific blog post, the problem was caused by outdated table statistics.

                                                              If you look at the query plan: https://explain.depesz.com/s/7qJI

                                                              You can see that the planner wrongly estimated the amount of rows the query would return (the “rows x” column and up/down arrows compared to “rows” column).

                                                              Vacuum in PostgreSQL is a process for reclaiming removed rows, preventing ID wraparound, updating statistics, dumping the pending list of a GIN index to the main tree and other maintenance tasks. This is a costly process and takes time.

                                                              There is also a VACUUM FULL which does full table rewrites. That’s something you almost never run or even want to run - it takes A LOT of time.

                                                              There are also autovacuum processes that are automatically ran to do the above in smaller incremental steps.

                                                              Now, in this particular case. In my humble opinion a vacuum was not needed. The author could have just ran ANALYZE TABLE order_items or even ANALYZE TABLE order_items purchase_order_number would have the same result in a fraction of the time.

                                                              I’m not a MySQL expert, but that database also has statistics (https://dev.mysql.com/doc/refman/8.0/en/analyze-table.html) and a step like this also might help the planner after large imports or adding new columns to big tables. I don’t see anything in the linked stackoverflow post indicating that MySQL would be immune to outdated statistics leading to an inefficient plan.