1. 7

    Congratulations on the project and a great blog post.

    You mention that you do not love the verbosity of errors, and give an example.

    You are not wrapping returned errors, but pass them up the stack as is. This is not a problem only because your application is tiny, database wrapper never does more than a single operation within a method and call stack extremely shallow. If you would have a complex schema and method GetLists would make several queries, not wrapping error would cause issues. You could not tell where the error comes from.

    http.Error(w, err.Error(), http.StatusInternalServerError) is probably never wise to do. Why would you leak out internal application information? What if there is some sensitive data in the error string? What should the user do with the error information anyway?

    If you want to always return 500 on error (which is a great oversimplification or error handling) you can use a different handler notation together with a wrapper.

    func x(fn func(w http.ResponesWriter, r *http.Request) error) http.HandlerFunc {
        return func(w http.ResponesWriter, r *http.Request) {
            if err := fn(w, r); err != nil {
                log.Printf(err)
                w.WriteHeader(http.StatusInternalServerError)
            }
        }
    }
    

    If you will handle your errors correctly (i.e. return ErrNotFound), your can improve x wrapper to return a proper status:

    switch err := fn(w, r); {
    case err == nil:
        // Respones was written.
    case errors.Is(err, ErrNotFound):
        w.WriteHeader(http.StatusNotFound)
    default:
        w.WriteHeader(http.StatusInternalServerError)
    }
    

    Another approach could be to make your errors implement http.Handler interface in order to write error information themselves. This is not the direction I would recommend, but since your project is small in scope an experiment might not be bad :)

    type errNotFound struct {
         entityName string
    }
    
    func (e errNotFound) Error() string {
        return e.entityName " was not found"
    }
    
    func (e errNotFound) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusNotFound)
        fmt.Fprintf(w, "%s was not found", e.entityName)
    }
    

    Since you should wrap your errors, you will need a helper function to extract the right one from a chain.

    1. 3

      I wrote a thing about how I handle errors in Go. The TL;DR is I have an error helper that lets me wrap errors in a status code and user message. If an error makes it to the top level handler without having been wrapped in a status code, the code defaults to 500 and the message defaults to Internal Server Error. I think it’s a pretty good system!

      1. 2

        These are good points, and thanks. Yeah, you’re right that I probably shouldn’t return err.Error() directly to the user – it was definitely a shortcut to avoid thinking about it for this project. :-) I’ve updated the post and code to avoid this.

        Regarding passing errors up directly: yes, it’s usually fine in a tiny app because as you say, the call stack is shallow. In languages with exceptions, the stack trace is always attached to the exception, so you get that “for free” (not in compute cost, but in developer time). I know there are libraries for Go which wrap errors and record a stack trace in the same way, but I haven’t used them. In practice for larger apps, I’d use fmt.Errorf("...: %w", err) or other forms of wrapping.

        Nice idea with the wrapper that wraps an error-returning function: I quite like the looks of that pattern. I might even play with it in this project on a branch to see how it works in practice.

        Thanks again for the interaction!

        1. 4

          In practice for larger apps, I’d use fmt.Errorf("...: %w", err) or other forms of wrapping.

          That is definitely the right way to go, and I totally understand why you wouldn’t bother with that for a tiny app!

          In languages with exceptions, the stack trace is always attached to the exception, so you get that “for free”

          That’s true, but it’s only ever a bare stack trace with no useful context. Wrapping errors in the idiomatic Go way encourages developers to add useful context: I find Go error messages are generally much more useful for debugging than stack traces. This is especially true when working with systems across a network, where a stack trace generated on one node would be almost useless.

          There’s a nice description of error handling in Go here: Error handling in Upspin

          1. 4

            The Upspin article is good, but it predates errors.As, so it’s a bit outdated IMO. I wrote a thing that builds on that article specifically about how to create error domains in a post-errors.As world: https://blog.carlmjohnson.net/post/2020/working-with-errors-as/

            1. 2

              That’s an excellent article, thanks for sharing it.

        2. 1

          A list of suggestions:

        1. 10

          I decided to play it safe and I went with Ubuntu 20.04, which was the latest release at the time. Historically I preferred Arch Linux and Fedora, but I wanted to minimize the setup efforts.

          Over the years, I have settled on a similar approach. For personal use I have a Void Linux installed, and I love it. It is minimal and tailored to my needs.

          For every company that I work for, I am installing a fresh system. It used to be Ubuntu, but in my opinion Ubuntu degraded, and now I am sticking to Linux Mint. I am replacing mate with i3 and copy all dotfiles. I want a minimal effort system that just works.

          1. 8

            I use Ubuntu with Regolith (https://regolith-linux.org/) for both work and personal project use. My work laptop is a Dell XPS 13 that came with Ubuntu pre-installed and my personal box is a System76 Merkat which also came with Ubuntu pre-installed. In both cases it “just works” (so far). I don’t have time anymore to debug OS issues.

            1. 2

              I was not aware of regolith, it looks great! I always wanted to use whatever comes by default configured, because I use mostly terminal. The only must-have is i3 and so far this required me to configure a lot after switching session from gnome/mate/xfce to i3.

              1. 1

                I’ve had to do close to zero configuration for regolith other than changing the default gap between tiles to fit my personal taste. It has a nice shortcut dialog (super-shift-?) for seldom used keybindings. After using it for a couple of years it is hard having to occasionally use an overlapping window system when I used other machines.

            2. 5

              Interestingly, while I also want to forget about OS, my path was exactly the opposite:

              I started with Ubuntu, and there things were always broken for unknown reasons. Switching to Arch was better: now things were broken, but because I had broken them. Finally, I switched to NixOS, and, after spending considerable time learning the thing, I’ve reached the state where everything works, I can’t break it, because the root is read only, and I can always rollback after update by choosing a different boot entry. For the past 4 years or so, I have been essentially running the same system which outlived a handful of machines. This is very different from all my previous experiences, where I had to re-install stuff to unbreak things couple of times a year.

              1. 4

                For work I still use Void - just works.

                1. 3

                  If you work for a lot of different companies and need to rebuild new environments with the same configs, why not use NixOS or Guix? It seems these are better suited to rebuilding from working stable states and sharing declarative configuration setups.

                  1. 2

                    That is a good question. I never considered it or thought about it. A new setup happens once or twice a year and takes less than 2 hours. I am not sure if investing in any of those would pay back.

                    1. 1

                      True. If traveling through their learning curves for your build systems and CI isn’t seen as side benefit, learning curve may not be worth the investment.

                1. 2

                  I have clicked the link, because I thought it was about CRUX.

                  1. 2

                    Is there anything in those ~100 lines of code worth looking at or discussing? I understand that the author might be proud of it, and that is great. But I fail to see how this link provides any value to lobsters community.

                    1. 3

                      People commenting here apparently don’t understand the context that forum message is being discussed. This is not a let’s move all the web backwards, it is more like let’s carve a niche corner, and move it back in time so our old Macs can render stuff. There are lots of retrocomputing enthusiasts running old Macs, Ataris, Acorns, etc out there. Many of those machines can’t handle modern cryptographic and webby workflows, having those niches adhere to older standards is better for compatibility while still allowing current day browsers on modern machines to view the same content.

                      The Web is retrocompatible with itself, this means that by following the practices outlined in that post, you can enjoy your weekend projects in your old Quadra running system 7, and still check the same content on your weekdays on your modern M1-based mac.

                      People saying that this is giving away all the advantages and security of the modern web don’t realise that this is not done in protest against those features, this is done because those machines can’t handle it. And before people say: “you should be using more modern machines!”, let me just say that there is a lot of joy and fun in these old machines and it is not up to anyone to tell what other people should spend their time with.

                      1. 1

                        This is a great summary. It feels to me that some comments did not think about the issue, but rather got triggered on a certain topic.

                        What I don’t want is a simple blog post that has to show a loading spinner while it downloads 50MB of static assets and then autoplays a video ad.

                        I think this is another great observation.

                        I also enjoy watching videos and pictures. I do believe there is also a place for canvas, websocket, WebGL etc. What I do not like is the abuse of technology at my cost. Instead of complaining and punishing fat pages or unprofessional developers, I would much more like promoting good content. Use a carrot instead of the stick.

                      1. 4

                        I used many solutions for offline tasks. Both Python frameworks and directly connecting to the broker. I worked with RabbitMQ, Kafka, Redis and PostgreSQL.

                        I do not quite understand why Redis is highly recommended as a message broker. If you are already having a Redis instance, depending on the requirements, it might be the right choice. But plain Redis requires some work in order to provide a solid task queue backend.

                        As a background task processing that is backing some web interface, a custom PostgreSQL backed task queue works amazing. Initially, I was a bit skeptical, because of what PostgreSQL is and how it works. Reading an old benchmark page gave me much needed confidence. If you are already using Posters and your task throughput is small enough (you have to judge this yourself) using PostgreSQL as a broker has so many advantages over any other solution.

                        If PostgreSQL is not enough for you as a task broker, you can tap yourself on the back, because you did a great job on building a popular product! Now take the afternoon to consider switching to another tool that will suite your needs better.

                        1. 2

                          I think the reason people recommend Redis is that for most average small websites it’d be replacing something really hard to setup, like RabbitMQ. Setting up a Redis instance is trivial. Most cloud providers I’ve used provide a hosted Redis solution, if you want to setup a highly-available server for some reason. A lot of people are using Redis as a replacement for memcached these days, so for most setups you already have a Redis instance. It’s also trivial - and incredibly light in resources - to reproduce your setup locally for development.

                          In other words: yes, Postgres might be good, but Redis carved the niche earlier and it’s super convenient and that’s why people recommend it.

                          1. 1

                            If you’re going to setup a hosted Redis on your cloud provider, there’s a good chance they provide an actual message broker or queue service already. On AWS there are several.

                            1. 2

                              being locked into a proprietary AWS service doesnt sound like a good time to me

                              1. 1

                                They’re not all proprietary - you can get managed Kafka (or something that’s API compatible), for example

                        1. 6

                          This blog post is absolutely great, and I use it as a starting point for a discussion about why not to use a custom testing interface (most of the time testify).

                          I feel very strong about testify. Negatively. Its use is a red light for me and a sign that the author might not think but rather force known from somewhere else solutions.

                          1. 9

                            I feel very strong about testify. Negatively. Its use is a red light for me and a sign that the author might not think but rather force known from somewhere else solutions.

                            This is a pretty strong statement regarding the usage of a test helper libary.

                            But if you think someone writing their own WithinDuration test helper method, for each library they write, is a good use of their time, and is somehow a signal for overall project quality (or of somehow not thinking?!), then I guess more power to you.

                            1. 2

                              “A little copying is better than a little dependency”

                              The cost of writing a set of Assert helpers for each project you own/maintain is zero.

                              1. 13

                                Sure. If time is worth nothing, then writing a set of helpers for every project is fine.

                                Oh, but then maybe you could share it across projects to save a bit of time/effort for any new project you start.

                                Maybe even open source it? Other people might even be interested in using it!

                                Oh wait.. Now we are back to square zero with it being bad?

                                That said, if you only need one (or a few, or several even) function, then sure. I agree that copying it around is better than adding a dependency. But if you ever reach a point where you have to update more than one project to add or fix a helper, then you are probably better off making it a dependency.

                                But use of a test helper library as somehow being a red light for overall project quality, sure seems dubious to me.

                                1. 2

                                  Sure. If time is worth nothing, then writing a set of helpers for every project is fine.

                                  The time it takes me to write those helpers is, without exaggeration, less than the time I spend waiting for VS Code to do whatever action in 1 day. It doesn’t enter into the cost accounting. The cost of a dependency, on the other hand, is real, and significant, and perpetual.

                                  I once heard a good rule of thumb: never import anything you could write in an afternoon. Assert is well below that threshold.

                                  But if you ever reach a point where you have to update more than one project to add or fix a helper, then you are probably better off making it a dependency.

                                  The only reason to add a helper to a project is if you need it; the only reason to update a helper in a project is if it’s causing problems in that project. There’s no situation I can think of where you have a bunch of similar/identical helpers in a bunch of projects you own/maintain, and you need to update them all.

                                  1. 17

                                    I’ve been using Go since before 1.0 was released. I have a lot of experience using the reflect package. I’m pretty sure I couldn’t write a good set of assert helpers in an afternoon.

                                    The funny thing here is that nobody seems to acknowledge that the assert helpers aren’t just about deleting some if statements. It’s also about the messages you get when a test fails. A good assert helper will print a nice diff for you between expected and actual values.

                                    testify is pretty dang close to what I would write. And while some dependencies have a perpetual cost, I’ve not experienced that with testify specifically.

                                    I usually like the “Go Way” of doing things, but this particular position is pretty Out There IMO.

                                    1. 3

                                      The funny thing here is that nobody seems to acknowledge that the assert helpers aren’t just about deleting some if statements. It’s also about the messages you get when a test fails. A good assert helper will print a nice diff for you between expected and actual values.

                                      I don’t see much value in rich assertion failure messages, most of the time. Literally this and nothing more is totally sufficient for 80% of projects.

                                      func Assertf(t *testing.T, b bool, format string, args ...interface{}) {
                                          t.Helper()
                                          if !b {
                                              t.Errorf(format, args...)
                                          }
                                      }
                                      
                                      1. 7

                                        You’re going to have a hell of a time debugging that on CI when all you have is “foobar equality failed” with no indication of what the unexpected value was to help you puzzle out why it works on your machine but not the CI server.

                                        I mean, more power to you but I’m not out to make my job any harder than it has to be. “expected: “test string” received: “TODO set this value before pushing test config”” is too easy a win for me to ignore, and god help you when the strings are piles of JSON instead. Then you’re really going to want CI to give you that diff.

                                        1. 3

                                          You’re going to have a hell of a time debugging that on CI when all you have is “foobar equality failed” with no indication of what the unexpected value was to help you puzzle out why it works on your machine but not the CI server.

                                          I hear this often enough, but it’s just never been my experience; I guess I’m asserting at a relatively granular level compared to most people.

                                          But it’s moot, I think, because if you need that specificity, Assertf lets you provide it just fine by way of the format string.

                                          1. 6

                                            I think the distinction is we are all likely writing different types of tests, that trade off different things.

                                            In tests that I write asserting on simple values, sure simple ifs get the job done for me.

                                            In tests of JSON outputs, or large structures, I find it more helpful to test equality of the entire thing at once and get a diff. It’s faster to review, and I get greater context, and the test will break if things change in the value I’m not testing.

                                            I find a lot of value in tests that operate at the top level of an application. Like tests that test the stdin and stderr/stdout of a CLI, or tests that test the raw request and response to an API. They catch more bugs and force me to think about the product from the perspective of the system interacting with it. I don’t think this is the only thing to test for though or only way to test.

                                            I know I find value in testify, it isn’t perfect like any code, but I dont think there’s a perfect practice about whether to use testify or not. It depends what you’re optimizing for and the type of assertions you’re making and inspecting.

                                            1. 5

                                              But it’s moot, I think, because if you need that specificity, Assertf lets you provide it just fine by way of the format string.

                                              It’s not moot, because usually by the time you realize you need it, you’re already looking at the failing test in CI. So now you need to roundtrip a patch to make your test more verbose.

                                          2. 8

                                            I don’t see much value in rich assertion failure messages, most of the time.

                                            Writing tests is part of my daily flow of programming, and so are failing tests. Not having to spend a bunch of time printf-ing values is a literal time saver.

                                            I’ve spent more years using plain go test than testify. We switched to testify at work a few years back and it paid for itself after a couple days.

                                            And I love how the goalposts have shifted here subtly. At first it was, “don’t reuse code that you could just write yourself in an afternoon.” But now it’s, “oh okay, so you can’t write it in an afternoon, but only because you value things that I don’t.” Like, have all the opinions you want, but “failure on test.go:123 is often totally sufficient” is just empirically wrong for me.

                                            Before testify, writing tests was a huge pain in the ass. And if it wasn’t a pain in the ass, it was a pain in the ass to read the error messages because the test didn’t print enough detailed information.

                                            Case in point, we’d have things like if !reflect.DeepEqual(x, y) { ... }, and when that failed, we’d be like, “oh what changed.” If x and y are big nested types, then printing out those values using the standard formatting specifiers is not that helpful. And I view the fact that needing reflect.DeepEqual (or go-cmp) in tests as a shortcoming in the language. There’s a convention for defining Equal methods which go-cmp reuses thankfully, but no other part of the language really recognizes the reality that, hey, maybe types want to define their own equality semantics independent of what Go does for you by default. And thus, Equal is not composable unless you go out of your way to recursively define it. Which, by the way, is an immediate footgun because it’s easy to forget to update that method when a new field is added. And it’s hard to write a good test for that.

                                            And don’t get me started on other shitty things. Like comparing values with time.Time in them somewhere. Or doing other things like, say, asserting that two slices have equivalent elements but not necessarily the same order. Oops. Gotta monomorphize that second one manually for each collection type you call for it. Or I could just use ElementsMatch and not think about it again.

                                            These are all problems that have come up for us in practice that have cost us time. Your “unpopular opinion” is crap in my experience.

                                            1. 2

                                              “failure on test.go:123 is often totally sufficient” is just empirically wrong for me.

                                              That’s totally fine! This isn’t a competition, we’re just sharing experiences. I think?

                                              Your “unpopular opinion” is crap in my experience.

                                              This honestly made me feel bad; I’m sorry to have put you off.

                                              1. 5

                                                I’m sorry too. Your comments in this thread came off as pretty dismissive to me and I probably got too defensive.

                                  2. 1

                                    Repetition is one of the claims that testify users are bringing. Irony is also often present, I believe to provide a bit more confidence.

                                    I think a preference to import as much external code or first thinking about any single problem and consider solving it without external code speaks well about what kind of developer you are. I do not find it productive to argue which approach is superior, because it often feels like beating a dead horse. I hope the right answer comes with experience.

                                    I have no idea what WithinDuration does, so I had to check this. Isn’t this function solving a very specific problem? Using this logic, I could claim that testify is garbage because it does not provide a function to check if a date is B.C. and I must write the assertion manually.

                                    It is easy to argue about abstract problems. Even easier if badly explained and with no context. Please notice that the blog post is very specific with examples and numbers.

                                1. 1

                                  Link is 404 :(

                                  1. 2

                                    t is missing at the end of the URL

                                    1. 2

                                      I raise you an l.

                                  1. 5

                                    I wish each blog was still maintaining a blogroll. I have found many interesting websites by looking at what people that I like to read are subscribing to themselves.

                                    Another dying approach is a planet. Planet Python is double cool, because not only it aggregates to Python related blogs, but also links to other planets!

                                    1. 15

                                      I don’t think this is a fair evaluation. Declaring something easy feels very personal anyway. To me, speaking the Polish language is easy.

                                      The example with list is not comparing which language is easier but which one allows to modify a slice with fewer characters. Is using single characters to represent operations in VimScript making the VimScript a simple language?

                                      Funny that the above argument is used in the next example against Go. Concurrency can be started with a single keyword, but this is complexity, because look how hard it can be. I believe what that example proves is that concurrency is hard and that the language does not abstract it away to hide the complexity of concurrency.

                                      I wonder if the author would consider the lack of exceptions or generics as a sign of simplicity or complexity.

                                      With this judgement, is Python a simple language? It has list.remove and list.pop. It supports asynchronous programming with async and await (and few other ways as well!). It has threading and multiprocessing packages in the standard library as well.

                                      Python even has a much-needed operator for matrix multiplicatoin. I bet in Ruby you have to use a old-school method or piggyback on some other operator! :P

                                      1. 9

                                        I don’t know about MySQL, this is not a database I would pick for any task really. I have successfully used Postgres as a task queue backend and I can definitely recommend it. I find using Postgres for a task queue a better choice than using RabbitMQ or Redis. This is possible thanks to locking with SELECT FOR UPDATE SKIP LOCKED. Assuming you are already using Postgres for your project, in addition to limiting the amount of dependencies, you have a great introspection tool: psql + SQL. Delayed tasks is also not an issue with Postgres.

                                        Please notice that I am talking about a task queue, that will be responsible for managing some sort of background tasks. I do not know if this setup might be a good choice for other queue use cases like a message bus or event sourcing.

                                        There are some projects that implement a task queue on top of Postgres, but implementing one from scratch is not a big effort as well.

                                        Already in 2013, Postgres was good enough for that use case. I imagine it only got better since then. https://news.ycombinator.com/item?id=21536698

                                        1. 6

                                          People were using MySQL (and Gearman) for this before Redis and RabbitMQ even existed (or were widely known), at least in PHP land, so if it could do it 15y ago (wouldn’t try with myISAM) it can probably still do it now.

                                        1. 2

                                          And this is why you don’t let buzzword kids/scientists push new language features into your language.

                                          They will be gone, and you’ll be stuck doing maintenance of these features forever.

                                          1. 4

                                            It is still in Pitch stage: https://forums.swift.org/t/differentiable-programming-for-gradient-based-machine-learning/42147

                                            The things got in, such as the DynamicMemberLookup, dynamicCallable, callable are generally useful. The PythonKit binding is actually pretty brilliant. The swift-jupyter notebook integration is passable and generally just wraps the main Swift REPL.

                                            Overall, I don’t think Swift as a language carried much baggage due to the ambition of S4TF.

                                            1. 4

                                              buzzword kids/scientists

                                              Are you referring to Chris Lattner as a buzzword kid? He created LLVM and Swift.

                                              1. 2

                                                Remember how Guido joined dropbox and dropbox started working on some SOOPER FAST python version which they later killed after they couldn’t make it work? And unless I’m outdated they still haven’t completely migrated from py2 -> 3? Chris Lattner isn’t the buzzword kid, it’s the fans.

                                                1. 2

                                                  I believe pyston is developed outside of Dropbox now, and it seems to be a python3 compatible implementation.

                                                  source: https://blog.pyston.org/2020/10/28/pyston-v2-20-faster-python/

                                                  1. 2

                                                    But it’s also closed-source, so it might as well be dead. They’re no longer contributing to the rest of the community.

                                                    I think that the parent post’s point was that corporate development of Python JITs seems to have a lot of conceptual overlap with corporate development of automatic differentiation; it’s useful technology in a particular climate, but requires a long-standing community tradition in order to stay maintained, and involves deep knowledge of how to write interpreters for the complete language under study.

                                                    1. 1

                                                      Right, they last sponsored the project in 2017. My point was that the project probably gained steam because the language designer was there.

                                                  2. 1

                                                    No, why?

                                                  3. 1

                                                    It wasn’t ever mainlined, right? Differentiable Swift was a first-party sanctioned fork afaik.

                                                  1. 1

                                                    As much as Erlang is an interesting language, I believe its niche is more narrow than the one Go fell into on the marketplace, and mostly in the same area of “microservices” (or I’d generalize it as “network services”). So I’m really confused what do you expect of it if you suggest you don’t feel challenged/fulfilled by this domain. Or if you do like this domain, just want to get into huge legacy systems (but do you really?), how about some C++ or Java? In other words, what kind of fulfillment are you looking for?

                                                    1. 2

                                                      I was wondering if by carefully choosing a technology, one can match projects fulfilling certain expectations. For example, using Go which I believe is still riding a hype-train, you can expect a lot of projects built by inexperienced programmers, writing X in Go (X being a previously used language). Many times I have seen a Ruby or JavaScript code written with Go syntax.

                                                      What I was curious is if picking an established environment like Erlang (not Elixir), one can expect that a project using Erlang will consist of professionals that are more interested in building quality software rather than chasing the latest trends.

                                                      I agree that the choice of the technology should be dictated by the problem. For this reason I considered Erlang.

                                                      1. 1

                                                        Sorry for a late reply. FWIW, as a single personal anecdote, I have a friend who’s working in a company using Elixir IIRC. Based on his complaints, given rarity of the technology on the market, the company (has to) hire people with no Elixir experience, and as a result, people there also seem to “write X in Elixir”, or at least they don’t use much of the good Elixir/Erlang practices in the code (such as supervisors etc.) But, again, personal anecdote, so YMM (hopefully) V ;)

                                                    1. 4

                                                      Learning Erlang by reading https://learnyousomeerlang.com/

                                                      1. 1

                                                        If that book leaves you looking for more, Programming Erlang is a classic: https://www.goodreads.com/book/show/808814.Programming_Erlang

                                                        1. 1

                                                          I am reading this too. I have slogged through the functional programming stuff and am about up to where the concurrency material starts. Hopefully it’s about to become a lot more interesting!

                                                          1. 0

                                                            Enjoy! Erlang is such a fun language. If you have Ruby experience I can recommend Elixir as well, they’re really closely related.

                                                          1. 5

                                                            It looks amazing. I look forward to getting a pinephone once my current phone dies.

                                                            I have looked at the repository and noticed that the programs are written in C. Would a different language be a better choice to write and maintain such an environment? It is hard for me to propose an alternative, I am mentioning this because it seems like C with all its advantages has many issues as well and maybe a fresh start (kind of new platform?) would allow using different tools. For tools as simple as https://git.sr.ht/~mil/sxmo-utils/tree/master/programs/sxmo_setpineled.c wouldn’t a shell script be a better choice?

                                                            Additnally, the file mentioned above looks suspicious to me – sprintf to a 80 bytes buffer regardless of the input, no free to malloc, program can return a negative value.

                                                            1. 3

                                                              no free to malloc

                                                              There is no point in freeing blocks at the end of a program

                                                              To your larger point, I’m not surprised that suckless attracts a lot of C hackers, and I’m not surprised that C hackers prefer to write certain types of tools in C instead of bash.

                                                              1. 2

                                                                The C is fine – slightly overly clever. But I agree – it’s a lot of code to run:

                                                                type=indicator
                                                                if [ $2 = white ]; then
                                                                    type=flash
                                                                fi
                                                                echo $3 > /sys/class/leds/$1:$type/brightness'
                                                                
                                                                1. 2

                                                                  Yeah, this specific program would likely be better as a shell script, especially since the C program calls sh to write to the brightness file anyway.

                                                                  1. 1

                                                                    especially since the C program calls sh to write to the brightness file anyway.

                                                                    That seems silly… are there any valid reasons why it wouldn’t just open the file and write to it directly?

                                                                2. 1

                                                                  i really want to explore using D for this, with rust or zig as slightly less compelling options. i feel like all three would be better than C.

                                                                1. 12

                                                                  Miniflux has let me fall in like with RSS again. I’m running it via docker-compose on an Ubuntu VM with Portainer managing most of my network services. I’ve got it behind Traefik so I can access it at my equivalent of http://miniflux.home.lan.

                                                                  I’ve put fewer than a dozen feeds in it in the last ~2 months and I’m reserving it only for things I don’t ever want to miss a single post from but also if I lose my feed list, I would be able to recreate it relatively quickly.

                                                                  1. 4

                                                                    I second Miniflux, it’s refreshingly simple and fast.

                                                                    1. 3

                                                                      I payed for the hosted version of miniflux. It’s literally just $15/year, which is a steal, and there’s a 15 day free trial so you can really make up your mind. I never knew I needed RSS, until miniflux.

                                                                      1. 1

                                                                        I also am a happy Miniflux user.

                                                                        My setup is a free Heroku instance. I am running it for a few months now and don’t see any issues. The only drawback is that I have to manually refresh the feeds. Deployment takes no time, upgrade is automatic whenever my GitHub repository is updated.

                                                                        1. 1

                                                                          Miniflux seems awesome, but I see that it currently only uses Fever API which is quite limited and FreshRSS/Google Reader API is still on the “requested” side :(

                                                                        1. 2

                                                                          Not strictly related to the topic, I thought this stnippet might be helpful to those that want to give twtxt a try. Below is a shell script that allows to create a new twtxt entry.

                                                                          #!/bin/sh
                                                                          
                                                                          set -e
                                                                          
                                                                          # read is too basic. No editing and no spellcheck.
                                                                          # read -p 'tweet: ' entry
                                                                          
                                                                          entryfile=`mktemp --suffix=_twtxt`
                                                                          # https://twtxt.readthedocs.io/en/stable/user/twtxtfile.html#format-specification
                                                                          vim \
                                                                                  -c 'set statusline=new\ twtxt\ tweet%=characters:\ %o' \
                                                                                  -c 'set filetype=twtxt' \
                                                                                  -c 'startinsert!' \
                                                                                  -c 'set spell' \
                                                                                  "$entryfile"
                                                                          
                                                                          # New line is not allowed. Instead of escaping it, replace with a space.
                                                                          entry=`tr '\r\n' ' ' < $entryfile`
                                                                          
                                                                          rm $entryfile
                                                                          
                                                                          if [ x"$entry" = "x" ]
                                                                          then
                                                                                  exit
                                                                          fi
                                                                          
                                                                          now=`date +%FT%T%:z`
                                                                          twtxtfile=${TWTXT:-$HOME/.twtxt.txt}
                                                                          
                                                                          echo "$now\t$entry" >> $twtxtfile
                                                                          

                                                                          In my i3 config, I have a shortcut for the above script to display a small window in the middle of the screen.

                                                                          bindsym $mod+t                 exec "xterm -class twtxtsh -T twtxtsh -g '120x4+100+400' -fs 20 -e twtxtsh"
                                                                          
                                                                          1. 2

                                                                            I really like this! I think twtxt will be the next step into alternatives to social media and a nice little script like this (which integrates with i3, bonus points) looks perfect. Thanks for sharing

                                                                          1. 9

                                                                            What about – hear me out – a 128kb club? One may say that 640K ought to be enough for anyone.

                                                                            I do agree that web needs a diet. I don’t think that making a list of websites helps anyone. Making a copy of a list of websites is even less smart.

                                                                            1. 7

                                                                              Don’t worry, the club to end all clubs is here: https://0kb.club

                                                                              1. 1

                                                                                I really started something here xD I love it!

                                                                              2. 2

                                                                                Why are you so angry? How does making this list hurt anyone?

                                                                                1. 22

                                                                                  I don’t think it hurts anyone, but I don’t think it helps either. In short, I think the question is really, what does it do? From my perspective, it does nothing.

                                                                                  And saying that it is basically useless does not mean I endorse heavy web pages. I am very much in favour of reducing bloat in sites. What I do find tiresome is the incessant complaining about it accompanied by poorly thought out rebellions, like trying to shame sites with arbitrary metrics or proposing dead-end protocols. Minimalism, in and of itself, should not be the goal. There needs to be a real incentive to making smaller sites that actually matters to those making them. Currently, no such thing exists. None of these proposals solve that or even acknowledge that it is a problem. It’s all predicated on the baseless belief that “smaller is better” will somehow win over hearts and minds. Rest assured that it won’t.

                                                                                  1. 3

                                                                                    There needs to be a real incentive to making smaller sites that actually matters to those making them

                                                                                    I think you misunderstand the motive for making larger sites in the first place.

                                                                                    These proposals won’t catch on in the large, because big companies are solely interested in profit. That is the only purpose of a company. They are not interested in technological innovation unless it services that goal. They are not interested in energy efficiency or any of the arguments you can make in favour of reducing web page size as long as the majority of their market has enough internet bandwidth and enough computing horsepower to cope. Actually, it’s beneficial to keep the status quo or build even more onto the tower of babel, not just for developers (despite the fact that alternative software could be written at a lower level of abstraction that does the exact same thing), but for companies and OEMs particularly – to propagate wasteful, costly technologies. Doing so ensures that, for example, telecommunication companies can continue getting grants for otherwise unnecessary infrastructure changes, or that OEMs can continue to sell laptops with ridiculously high ‘horsepower’ that really do not do anything different task-wise to laptops from a decade ago, and means Google et al can continue their trend of locking everyone in to Their Software™. It ensures that the wheel keeps spinning, if you will.

                                                                                    But the point isn’t that they catch on in the large, and it isn’t to ‘win hearts and minds’[0] the point is to pull back to not just a more minimalist web, but also to carve out new spaces as alternatives to a system that has been thoroughly branded, commoditized, and marketed. To create spaces that feel more personal, more familar, and are hand-crafted rather than Delivered and Targeted For You™

                                                                                    Sure, these new proposals won’t catch on ‘in the large’. That’s not the point here, at all.

                                                                                    [0] Whatever that means – seriously, there is no chance, at this time, of a new protocol taking hold regardless of the features it boasts. At least in the wide sense that you seem to mean.

                                                                                    1. 3

                                                                                      Sure, these new proposals won’t catch on ‘in the large’. That’s not the point here, at all.

                                                                                      Then they are doomed to obscurity and the constant complaining about the bloated web will continue.

                                                                                      1. 2

                                                                                        And once again, that is not the point.

                                                                                        The fact that they are creating new communities, new groups of specific styles of ideas, is valuable. Those connections with other people are valuable.

                                                                                        And regardless of that, if you are only concerned with the narrative of “we need to build the future and nothing else has any value”, it’s easy to show that time and time again, historically, small communities with ‘weird’ ideas serve to be melting pots for the technology of the future. We see this with places like, Xerox Parc, CSAIL, some Usenet sub-communities, LambdaMOO, etc.

                                                                                        Places like 2f30, suckless, 9front, et al led to the creation of Wayland, to a certain degree, for a modern example. Javascript and Python were influenced by Lisp, despite the fact that Lisp as a whole did not grow into an industry standard. Hell, despite my particular dislike about the borderline-abuse that goes on within the Merveilles community, they push out pretty good and interesting technology on the regular – orca has itself altered how a fair chunk of people create music interactively.

                                                                                        It’s not about creating the future directly, it’s about being able to communicate with people more directly and explore ideas that you wouldn’t have otherwise, and enjoying building unique spaces and connections with other people that are more genuine than those an algorithm has selected for you. It’s about creating spaces in which we can think differently, and from within that space, those ideas will in some shape or form, influence the future.

                                                                                    2. 1

                                                                                      I’ve had similar thoughts but didn’t have the words to express them as well. Lists like these are cool and all but like, so?

                                                                                      I’m more interested in things like code golfing. Don’t just have a minimal site, do something fantastical that only requires a small amount of scripting. That is impressive.

                                                                                1. 1

                                                                                  Linked document is about designing programs top-down, not writing code top-down. The difference might seem meaningless, but I think it is huge.

                                                                                  I am writing my Go code top-down. An alternative is for example Python PEP8 - imports first, then global variables, classes, functions.

                                                                                  Here is an example of a code written top-down. You start reading at the top of the file, and you learn details of the implementation as you progress.

                                                                                  func NewFoo() Foo { 
                                                                                      return Foo {} 
                                                                                  }
                                                                                  
                                                                                  type Foo struct {
                                                                                  }
                                                                                  
                                                                                  func (f Foo) Stuff() string { 
                                                                                      return fooStuff 
                                                                                  }
                                                                                  
                                                                                  const fooStuff = "foo-stuff"
                                                                                  

                                                                                  Here is the save code as above, not structured top-down but with a pep8-like style. It does not matter how the functionality is implemented, the position of code in the file is dictated by the type of instruction.

                                                                                  const fooStuff = "foo-stuff"
                                                                                  
                                                                                  type Foo struct {
                                                                                  }
                                                                                  
                                                                                  func NewFoo() Foo { 
                                                                                      return Foo {} 
                                                                                  }
                                                                                  
                                                                                  func (f Foo) Stuff() string { 
                                                                                      return fooStuff
                                                                                  }
                                                                                  
                                                                                  1. 9

                                                                                    I’m gonna be that guy… it doesn’t work for me because I have JS off.

                                                                                    I use a different browser for things that legitimately require JS, but this is just static content as far as I can tell.

                                                                                    1. 10

                                                                                      I’m gonna be that guy… it doesn’t work for me because I have JS off.

                                                                                      Did you try enabling awk?

                                                                                      1. 3

                                                                                        *gawks at thread*

                                                                                      2. 5

                                                                                        Worse yet, not only it doesn’t work without JS, it doesn’t even tell the visitor that it needs JS to work—just looks broken.

                                                                                        The idea to make a website for AWK the language may be a good one, though.

                                                                                        1. 3

                                                                                          … as long as it’s AWK, not GNU Awk :^)

                                                                                          1. 1

                                                                                            That reminds me of The Power of GNU AWK.

                                                                                        2. 2

                                                                                          I had the same issues - no content without JS.

                                                                                          On one hand discussing the weird JS requirement has nothing to do with the original link. On the other hand being verbose about a bad page now is the only way we can influence what kind of quality we expect.

                                                                                          1. 0

                                                                                            It even does not work with html off! I just wanna text!