1. 3

    The sourceforge link might indicate otherwise, but this is actively developed: https://github.com/rocky/remake. (I’m curious what sourceforge offers these days – are they rehabilitated from that malware affair?

    Regarding remake itself, I wonder if there are plans to upstream some of these changes, and/or whether there’s any history of attempting to work with the GNU make maintainers.

    1. 2

      )

      1. 1

        BTW I chatted with the author Rocky up on the Oil zulip and took some notes on debuggers in dynamic languages:

        https://github.com/oilshell/oil/wiki/Implementing-Debuggers

        He also wrote the bash debugger, so I hope to incorporate that knowledge into Oil. I mostly debug shell as a REPL, but there are use cases for a debugger.

      1. 7

        Rust just joined the ranks of Paxos and monads.

        1. 11

          Not quite. The title may refer to “easy english”, but what’s meant is the reduced linguistic subset often also called “simple” or “basic” English.

          “Paxos made simple” is not such a text.

          1. 3

            You mean the subset used in https://simple.wikipedia.org/wiki/Main_Page ?

            1. 3

              Yep, exactly that.

            2. 1

              often also called “simple” or “basic” English.

              Isn’t “Basic English” the name? Why are you putting it in quotes?

              1. 4

                Quotes for readability. Basic is the correct name, but e.g. the Wikipedia itself uses and lists simple as another way to refer to it (and has a “Simple English Wikipedia”).

                1. 4

                  It would appear the word “basic” isn’t in the Basic English vocabulary (but “simple” is. And “base” - but I don’t think there’s a (grammar) rule to define/derive “basic” - except maybe the “use words from industry etc.” (special terms).

                  So it would make sense that the Basic English term for Basic English is Simple English…

                  1. 1

                    Ha, that’s a great find!

            3. 11

              The borrow checker is like a burrito.

            1. 2

              Do any commercial VPN providers offer WireGuard support yet? I remember reading that ExpressVPN was working on it, but haven’t heard any news in a while.

              1. 17

                mullvad.net has had it for quite some time, not sure about any others (as in I’ve not tried to check it with others)

                1. 3

                  I also use mullvad. WireGuard works great especially as VPN on my iPhone.

                  1. 1

                    I’ve been using Mullvad for several months not, it’s wonderful

                    1. 3

                      NordVPN

                      1. 0

                        Though I would want to use a provider who is known for hiding their ownership information and their leadership behind some Panamanian shell company.

                      2. 1

                        ExpressVPN was working on it

                        They built their own protocol on DTLS.

                      1. -1

                        Can anyone recommend some material describing concrete motivations for adding generics to Go? I’m aware of the abstract idea that you need generics in order to build data structures that work with different types, but is there a real-world setting where this is actually better than just copying code and doing s/type1/type2/g? My sense is that projects that use complex data structures almost always customize the data structures in a way that depends on the data type being stored.

                        1. 19

                          I hope it’s not that hard to imagine wanting different data structures than hash maps; maybe your problem fits better into a binary search tree for example.

                          Well, I for one usually don’t feel like implementing my own red-black tree, so I would like to just grab a library. That library will be much nicer to use if I can just make an RBTree<string, MyFoo>. I certainly wouldn’t want to copy/paste an int->string red-black tree into some file(s) and judiciously search/replace until I have a string->MyFoo tree (and then do the same when I need an int->float tree).

                          1. 0

                            That makes sense, but I am still looking for some grounding in actual programming practice. Is there a use of a red-black tree that would not warrant customizing it for the storage type? Or one where it would make sense to add a library dependency rather than copying the RB tree code?

                            1. 7

                              How do you write a library that provides a Red-Black tree that can in principle work with many different client types without generics? This isn’t a rhetorical question, I don’t know Go and I genuinely don’t know how you would implement this kind of library in Go without generics.

                              1. 6

                                Go’s sync.Map (concurrent hashmap) is an actual real world example of this, and it uses interface{}, akin to Java’s Object.

                                1. 24

                                  Right, that’s a great example. Because it uses interface{}, it:

                                  • Requires all keys and values to be heap allocated, leading to worse performance, worse memory usage, and worse memory fragmentation. Requiring two heap-allocated ints to store one value in an int->int concurrent hash map is unacceptable for many uses.
                                  • Is less ergonomic, requiring a cast every time you want to use a value.
                                  • Provides no type safety. (I imagine this one will be the least convincing to Go programmers, since Go generally expects the programmer to just not make mistakes)
                                  1. 3

                                    This brings me back to C++Builder 3 back in the 90s. To use a list, you had to create a class derived from a kind of TItem class to be able to store things. Why anyone would want to go back to that in productive code boggles my mind.

                                    1. 1

                                      I’m using a sync.Map (for its concurrency support - I have many goroutines writing map entries, and another goroutine periodically ranging over the entire map to dump it to a json file).

                                      However I know the types I write to the map, I have no need for interface{}.

                                      Am I better off with a real typed map + using sync.RWLock/mutex/etc. directly (in a custom struct)? Performance-wise.

                                      1. 1

                                        I don’t know, you would have to benchmark or measure CPU or memory usage. The sync.Map documentation suggests that using a regular map + mutexes could be better though: https://golang.org/pkg/sync/#Map

                                        The Map type is specialized. Most code should use a plain Go map instead, with separate locking or coordination, for better type safety and to make it easier to maintain other invariants along with the map content.

                                        The Map type is optimized for two common use cases: (1) when the entry for a given key is only ever written once but read many times, as in caches that only grow, or (2) when multiple goroutines read, write, and overwrite entries for disjoint sets of keys. In these two cases, use of a Map may significantly reduce lock contention compared to a Go map paired with a separate Mutex or RWMutex.

                                        If your usage falls outside of the two use cases which sync.Map is optimized for, it would absolutely be worth looking into replacing your sync.Map with a regular map and a mutex.

                                        I suppose it becomes a question of which has the biggest performance penalty for you, heap allocation + indirection with sync.Map or lock contention with regular map + mutex?

                                        (Also, in most cases, this probably doesn’t matter; make sure you’re not spending a long time improving performance in a part of your code which isn’t actually a performance issue :p)

                                        1. 1

                                          Right - the code “just works(TM)” and it takes around 0.5 seconds to render the JSON file every minute (which I track with metrics just to be safe) so it should be fine to keep as is. This is just a for-fun conversation.

                                          or (2) when multiple goroutines read, write, and overwrite entries for disjoint sets of keys. In these two cases, use of a Map may significantly reduce lock contention compared to a Go map paired with a separate Mutex or RWMutex.

                                          I definitely remember reading this sentence and it made me choose sync.Map because it sounds like my usecase. But like you say if I don’t measure it’ll be hard to tell.

                                  2. -1

                                    I don’t know and I didn’t think you could. I’m asking for an example use of an RB tree where using a library would make sense.

                                    1. 6

                                      Here is a popular Go RB tree implementation https://github.com/emirpasic/gods/ that uses Interface{} for the key and value types. Just search github for uses of it… With generics, users of this library would get greater typesafety.

                                      https://github.com/search?q=%22github.com%2Femirpasic%2Fgods%2Ftrees%2Fredblacktree%22&type=Code

                                      1. -2

                                        okay. except i don’t know how to search github for uses of it and your search link brings me to a login page :(

                                        1. 4

                                          To short-circuit this:

                                          At a previous job, I worked on a tool that started various services. The services had different dependencies, each of which needed to be started before the service. We wanted to be able to bring them up with as much parallelism as possible, or have a flag to launch them serially.

                                          A simple approach to doing this correctly is modeling the dependencies as an acyclic graph (if it’s a cyclic graph, you’ve got a problem — you can never bring the services up, because they all depend on each other). To launch them in parallel, launch each one that has its dependencies met. To launch them serially, topologically sort the graph into an array/list/whatever and launch them one by one.

                                          A generic graph implementation would be very useful, as would a topological sort that worked on generic graphs. With Go, you can’t have one that’s type-safe.

                                          Another great use case for graphs: visualizing dependency graphs! You can have an open source graph visualization library, build a graph of whatever it is you’re trying to visualize, and pass it to the library and get a nice visualization of the data.

                                          Graph data structures can be quite useful. Supporting generics makes them type-safe, so you catch errors at compile time instead of runtime. Some other examples of the usefulness of graphs:

                                          • Graphs of friends at a social network (I currently work at one, and we use generic graph data structures all over the place — graphs of people to people, graphs connecting people and photos they’re tagged in, etc)
                                          • Network topology graphs
                                          • Graphs of links between documents

                                          etc.

                                          And it’s not just graphs. How do you write a type-safe function that takes in a list of possibly-null items, and returns a new list with the nulls stripped out? How about a function that takes a map and returns the list of its keys? In Golang, the answer is always copy-paste or give up type safety. In languages with generics, you can trivially write these functions yourself if they’re not in the standard library.

                                          1. 1

                                            thanks, this is a good motivating example.

                                            1. 1

                                              Huh. It had not occurred to me that github search would require a login.

                                  3. 11

                                    To turn the question around, why would you want to manually copy/paste code all over the place when the compiler can do it for you? And while I personally think “DRY” can be over done, not having the same (or very similar) code copy/pasted all over the place seems like a big practical win.

                                    As far as customizing specific data structure and type combinations, most languages with generics have a way to do that, and I’d bet the Go designers thought of it.

                                    1. 2

                                      Copy / paste has got it’s own problems, but it lets you avoid a ton of complexity in the toolchain.

                                      Toolchain development is all about tradeoffs. For instance, I use Typescript; the reference implementation is featureful, but slow to boot, so it keeps a background process alive to cache the heavy lifting, which accumulates state and introduces subtle confusions (eg type errors that don’t exist) until it’s restarted.

                                      For some problem spaces, the problems introduced by copy/paste pale in comparison to the problems introduced by slow, stateful compilers.

                                      1. 7

                                        Copy/paste vs generics is unrelated to compiler bugginess.

                                        If you carefully pick TypeScript as the comparison point, you can make the case that a buggy toolchain is bad (not that most users care, they just restart the compile process when it starts to go bad).

                                        But if you were to pick say ReasonML for comparison, you could say that it’s possible to have a solid generics implementation (much less copy-pasting) and a super-fast, accurate compiler.

                                        I.e. you can have both buggy and non-buggy compilers supporting generics. Hence, unrelated.

                                        1. 2

                                          ReasonML is great!

                                          That said, while the relationship is indirect, it’s there. Adding complexity is never free. It didn’t cost ReasonML speed or reliability, but it costs maintainers time and makes every other feature more difficult to add in an orthogonal way.

                                          1. 2

                                            In the scheme of things, is it more important to have a super-simple compiler codebase, or is it more important to put more power and expressiveness in the hands of users? Note that every mainstream language that started without generics, has now added it.

                                            1. 1

                                              IMO, there’s such a thing as a right time to do it.

                                              In the early years it’s more important to keep the simplicity - there aren’t that many users and you’re still figuring out what you want the language to be (not every feature is compatible with every approach to generics).

                                              Once you’re ready to start generics you need to answer questions like - do you want monomorphisation or lookup tables? Is boxing an acceptable overhead for the improved debugging ergonomics?

                                              1. 1

                                                It seems like Go has been going through exactly the process you’re describing.

                                            2. 2

                                              I think these comparisons are a bit unfair: isn’t Typescript self hosted, whereas ReasonML is written in OCaml? It seems like Typescript would have a very hard time competing.

                                              1. 3

                                                Being able to use lots of existing OCaml bits is a huge advantage.

                                                Typescript has been able to compete due to the sheer number of contributors - MS pays quite a large team to work on it (and related stuff like the excellent Language Server Protocol, VScode integration).

                                                However, large teams tend to produce more complex software (IMO due to the added communications overhead - it becomes easier to add a new thing than find out what existing thing solves the same problem).

                                                1. 1

                                                  I should clarify my comment was more about comparing performance of the two languages.

                                                  OCaml is a well optimized language that targets native machine code so tooling built in OCaml should be more performant than tooling built in Typescript. As a result, it’s hard to compare the complexity of either tool by the performance of the tool. It’s apples and oranges.

                                                2. 2

                                                  isn’t Typescript self hosted, whereas ReasonML is written in OCaml? It seems like Typescript would have a very hard time competing.

                                                  That’s a strange argument. If it were very hard for them to compete why would they not use OCaml as well, especially since its contemporary alternative, Flow, was written in OCaml too? Or why would they not make TypeScript as good as a language for writing TypeScript in as OCaml is?

                                                  1. 1

                                                    My comment was more about performance, but it wasn’t very clear. It’s hard for Typescript, which is compiled to Javascript and then interpreted/JITed, to create tooling that’s as fast as a language that builds optimized native code.

                                                    Given that Typescript is self hosted it has the advantage that community involvement is more seamless and I don’t want to downplay the power that brings.

                                              2. 0

                                                But compilers that support generics are more likely to be buggy. That’s a relation.

                                                1. 2

                                                  Any source for this rather surprising assertion?

                                                  1. 0

                                                    generics are feature that requires code to implement; code can contain bugs.

                                                    1. 1

                                                      But a self-hosting compiler with generics is likely to be less verbose (because generics) than one without, so it should be less buggy.

                                                      1. 1

                                                        i guess you can’t prove it either way but IME the complexity of algorithms is more likely to cause bugs than verbosity.

                                              3. 5

                                                I think Typescript is a straw man. Does this Go implementation of generics slow down the compiler a noticeable amount? There’s nothing inherent to generics that would make compiling them slow.

                                                On the other hand, copy/pasted code is an ever increasing burden on developer and compile time.

                                              4. -2

                                                You are imagining a code base where the same complex data structure is instantiated with two different types. Is that realistic?

                                                1. 5

                                                  You are imagining a code base where the same complex data structure is instantiated with two different types. Is that realistic?

                                                  Realistic enough that the Linux kernel developers went through the hassle of developing generic associative arrays, circular buffers, and other generic data structures using void*.

                                                  And so did Gnome with GLib, which provides generic lists, hash tables, and trees, along with several others structures, also using void*.

                                                  And the standard libraries of most modern languages include reusable and generic sequence and associative data types, and some times significantly more than that.

                                                  For most data structures, though, focusing on a single code base gives too narrow of a view. Generics allow libraries of data structures to be created, so even though a single code base only use one R* tree (or whatever), that R* tree library can be used as-is by any number of projects.

                                              5. 8

                                                The Abstract and Background sections of the draft design doc touch on the motivations. Additionally, each section describing a dimension of the design usually mentions, at least briefly, the motivation for that feature.

                                                1. 8

                                                  Here is an example that I’ve wanted for ever, and can finally do. Higher order combinators that you can leverage first class functions with!

                                                  Generic map, in go

                                                  1. 1

                                                    That’s the type of thing I have seen as a justification, but I don’t get why that’s so important. Can’t you just use a for loop?

                                                    1. 22

                                                      “Can’t you just …” goes forever. “Can’t you just write your for loop with labels and jumps in assembly?”^^

                                                      For me, it’s all about abstraction. Having low level combinators, like this, that I can compose to build higher level abstractions in a generic way is wonderful.

                                                      ^^: See also whataboutism.

                                                      1. 3

                                                        I’m not sure that composing from higher level abstractions is always such a good idea. I like both Go (hobby projects) and Rust (work!) but I still fell that most of the time I prefer this level of abstraction:

                                                        type Server struct {
                                                        ...
                                                            Handler Handler // handler to invoke, http.DefaultServeMux if nil
                                                        ...
                                                        }
                                                        type Handler interface {
                                                            ServeHTTP(ResponseWriter, *Request)
                                                        }
                                                        

                                                        from this:

                                                         pub fn serve<S, B>(self, new_service: S) -> Server<I, S, E>
                                                            where
                                                                I: Accept,
                                                                I::Error: Into<Box<dyn StdError + Send + Sync>>,
                                                                I::Conn: AsyncRead + AsyncWrite + Unpin + Send + 'static,
                                                                S: MakeServiceRef<I::Conn, Body, ResBody = B>,
                                                                S::Error: Into<Box<dyn StdError + Send + Sync>>,
                                                                B: HttpBody + 'static,
                                                                B::Error: Into<Box<dyn StdError + Send + Sync>>,
                                                                E: NewSvcExec<I::Conn, S::Future, S::Service, E, NoopWatcher>,
                                                                E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
                                                            {
                                                            ...
                                                        

                                                        Don’t get me wrong, i like type level guarantees and I can see flexibility here, but in my experience with c++, rust and haskell is that generic programming often ends up complicating things to a degree that I personally don’t like.

                                                        1. 1

                                                          I think this is going to be a balance that the community has to find. I don’t regularly program in rust, but I’d be quite surprised if it wasn’t possible to get something close to the Go http API in it. The example you pasted seems complicated for the sake of being complicated. In theory, the Go community has been drilled into thinking in terms of the littlest abstraction that’ll work, which maybe makes it possible to generally avoid generic APIs that don’t actually need to be?

                                                        2. 3

                                                          “Can’t you just” does not go forever. It is a simpler way to say that the alternative is not significantly harder than what’s proposed. Is there some type of task that would be doable using a generic map but unreasonably hard using for loops?

                                                          I feel like Go was designed from the ground up to be written in an imperative style, and composing first order functions is more of a functional style of coding. If I understand, without generics you would be nesting for loops rather than composing map functions, which is no more difficult to understand or write.

                                                          I don’t follow the connection to whataboutism.

                                                          1. 2

                                                            I think it’s fine for your style of writing code to be to use loops and conditionals instead of map and filter. I think it’s a fine way to code that makes more sense in an imperative language. Straight for loops and while loops with if statements inside them is just better, more easily understandable code in an imperative language, in my opinion, than .map(...).filter(...).map(...) etc.

                                                            1. -1

                                                              Incidentally there is a repo wherein Rob Pike expresses his attitude towards this style of coding:

                                                              https://github.com/robpike/filter/

                                                              I wanted to see how hard it was to implement this sort of thing in Go, with as nice an API as I could manage. It wasn’t hard.

                                                              Having written it a couple of years ago, I haven’t had occasion to use it once. Instead, I just use “for” loops.

                                                              You shouldn’t use it either.

                                                              1. 2

                                                                I mean… that’s like … one man’s opinion… man. See also.

                                                                Generics are going to create a divide in the Go community, and it’s going to be popcorn worthy. There’s no point of adding Generics to the language if this filter thing “shouldn’t be used,” and the community rejects the abstractions that Generics provide.

                                                                This divide is easily already seen in the community as it relates to test helpers. On the one hand, there’s a set of developers that say “stdlib testing is more than enough.” On the other hand, there are people who want the full testing facilities of junit, with matchers, lots of assert style helpers, etc. Who is right? They all are, because those things work for their respective teams and projects.

                                                                This general dogmatic approach to language idioms is why I call it “idiotmatic” Go.

                                                                1. -1

                                                                  I suppose if Ken and Rob wanted generics they would’ve put them in the original language, and there wouldn’t be this controversy. Time to go back to learning Erlang which seems old and dusty enough to not have big language changes and drama.

                                                            2. 16

                                                              You can’t pass a for loop to anything, you can only write it where you need it. Sure, toy examples look like toy examples, but the fact remains that Go has first-class functions, which should be a nice thing, but it doesn’t actually have a type system rich enough to express 90% of the things that make first-class functions worth having.

                                                              1. -1

                                                                You can’t pass a for loop to anything, you can only write it where you need it.

                                                                right, so the example code could be done with a for loop no problem. is there a more motivating example?

                                                                it doesn’t actually have a type system rich enough to express 90% of the things that make first-class functions worth having.

                                                                how do you mean?

                                                              2. 3

                                                                Consider composing multiple transformations and filters together. With multiple for loops you have to iterate over the array each time, while by composing maps you only need to iterate once.

                                                                1. 3

                                                                  Just compose the operations inside the loop.

                                                                  for x in y:
                                                                      ...f(g(x))...
                                                                  
                                                                  1. 2

                                                                    That works in some cases, but it’s pretty easy to find a counter example, too.

                                                            3. 7

                                                              In terms of collections, the truth is most of the time a map/slice is a good option. Here’s my top two favorite use cases for generics in go:

                                                              1. Result<T> and functions that compose over them.
                                                              2. Typesafe versions of sync.Map, sync.Pool, atomic.Value, even a rust like Mutex
                                                              1. 5

                                                                Oh man. I hadn’t even considered a better way to do error handling, eg. a Result type. People are gonna get so mad.

                                                                1. 5

                                                                  Generics isn’t enough to do what people want to do with error handling 99.99% of the time, which is to return early. For that, you either need a macro, such as the aborted try proposal, or syntactic sugar for chaining such “functions that compose over them” (like Haskell’s do notation).

                                                                  Otherwise you end up with callback hell à la JavaScript, and I think nobody wants that in Go.

                                                                  1. 4

                                                                    I was more thinking of something where the if err pattern is enforced via the type system. You’re still not getting there 100%, you could get reasonably close, with a generic Result type that panics when the wrong thing is accessed, forcing you to check always or risk a panic.

                                                                    r := thing()
                                                                    if r.HasError() { handleError(r.Err()) }
                                                                    else v := r.Val() { handleSuccess(v) }
                                                                    

                                                                    And of course it’s easy to question why this is interesting until you do chaining of things, and get a full on, type safe Result monad.

                                                                    r := thing().andThen(func(i int) { ... }).andThen(func(i int) { ... })
                                                                    if r.IsErr() {
                                                                       handleErrForWholeComputation(r.Err())
                                                                    } else {
                                                                       handleSuccessForWholeComputation(r.Val())
                                                                    }
                                                                    

                                                                    The alternative can be seen in things like this where you skirt around the fact that you can’t generically accept a value in one of those called functions. This is also why I said people are going to get so mad. These things are confusing to people who haven’t dealt with them before, and will make Go much more expressive, but less easy to grok without effort.

                                                              2. 5

                                                                but is there a real-world setting where this is actually better than just copying code and doing s/type1/type2/g

                                                                All of them. Copying code manually is one of the worst things you can do in software development. If it weren’t, why even bother writing functions, ever?

                                                                My sense is that projects that use complex data structures almost always customize the data structures in a way that depends on the data type being stored.

                                                                The fact that libraries exist that don’t customise in such a way in languages with generics would disprove that notion.

                                                                1. 6

                                                                  one of the worst things you can do in software development

                                                                  For me that’s “making things unreadable for whoever comes after you”. And sometimes copying a bit of code is the optimal solution for avoid that.

                                                                  1. 0

                                                                    but is there a real-world setting where this is actually better than just copying code and doing s/type1/type2/g

                                                                    All of them. Copying code manually is one of the worst things you can do in software development. If it weren’t, why even bother writing functions, ever?

                                                                    I disagree with your implication that the use of functions means code should never be copied. For example if you want to use strlcpy() in a portable C program, it makes more sense to put a copy in your source tree rather than relying on an external library. An extra dependency would add more headache than just copying the code.

                                                                    My sense is that projects that use complex data structures almost always customize the data structures in a way that depends on the data type being stored.

                                                                    The fact that libraries exist that don’t customise in such a way in languages with generics would disprove that notion.

                                                                    That’s why I said “almost always.” And remember that the existence of a library doesn’t mean it is used with any frequency.

                                                                  2. 3

                                                                    Suppose you have a structure parametrizable by types T1, T2. You’re writing it in Go, so you assume that it’s ok if T1=string, T2=int. Also, in some of the places, you were using int for purpose unrelated to T2 (ie. if T2=foo, then there are still ints left in the source). Another programmer wants to copy-paste the code and change some types. How does he do it?

                                                                    1. 2

                                                                      I think “this would make copy-pasting code harder” is not so compelling an argument. One of the major points of introducing generics is that it would eliminate much of the present need for copy/paste in Go.

                                                                      1. 2

                                                                        Yes it would be harder than a search-and-replace, but that is still abstract and unrelated to any real-world use case.

                                                                        Yes, I’m just counterpointing the parent commenter’s argument. I know the value of generic structures.

                                                                      2. -1

                                                                        Yes it would be harder than a search-and-replace, but that is still abstract and unrelated to any real-world use case.

                                                                    1. 18

                                                                      What has been the problem with Python/Flask/SQLA?

                                                                      1. 28

                                                                        Python: the size of the codebase and number of moving parts has reached a point where the lack of static typing has become the main source of programmer errors in the code. There are type annotations now, but they don’t work very well IMO, are not used by most of our dependencies, and would be almost as much to retrofit onto our codebase as switching to a type-safe language would be. The performance of the Python VM is also noticably bad. We could try PyPy, but again… we’re investing a lot of effort just to stick to a language which has repeatedly proven itself poorly suited to our problem. The asyncio ecosystem helps but it’s still in its infancy and we’d have to rewrite almost everything to take advantage of it. And again, if we’re going to rewrite it… might as well re-evaluate our other choices while we’re at it.

                                                                        Flask: it’s pretty decent, and not the main source of our grief (though it is somewhat annoying). My main feedback for Flask would be that it tries to do just a little bit too much. I wish it was a little bit more toolkit-oriented in its design and a more faithful expression of HTTP as a library.

                                                                        SQLAlchemy: this is now my least favorite dependency in our entire stack. It’s… so bad. I just want to write SQL queries now. The database is the primary bottleneck in our application, and hand-optimizing our SQL queries is always the best route to performance improvements. Some basic stuff is possible with SQLAlchemy, simple shit like being smart about your joins and indicies, but taking advantage of PostgreSQL features is a pain. It’s a bad ORM - I’m constantly fighting with it to just do the shit I want it to and stop dicking around - and it’s a bad database abstraction layer - it’s too far removed from Postgres to get anything more than the basics done without a significant amount of grief and misery. Alembic is also constantly annoying. Many of the important improvements I want to do for performance and reliability are blocked by ditching these two dependencies.

                                                                        Another problem child that I want to move away from is Celery. It just isn’t flexible enough to handle most of the things I want to do, and we have to use it for anything which needs to be done asyncronously from the main request handling flow. In Go it’s a lot easier to deal with such things. Go also allows me to get a bit closer to the underlying system, with direct access to syscalls and such*, which is something that I’ve desired on a few occasions.

                                                                        For the record, the new system is not without its flaws and trade-offs. Go is not a perfect tool, nor GraphQL. But, they fit better into the design I want. This was almost a year of research in the making. The Python codebase has served us well, and will continue to be useful for some time to come, in that it (1) helped us understand the scope necessary to accomplish our goals, and (2) provided a usable platform quickly. Nothing quite beats Python for quickly and easily building a working prototype, and it generally does what you tell it to, in very few lines of code. But, its weaknesses have become more and more apparent over time.

                                                                        * Almost. The runtime still gets on my nerves all the time and is still frustratingly limiting in this respect.

                                                                        1. 9

                                                                          Thanks for responding. I think static typing in Python works really well once configured so I’m surprised to hear you say that. I think it’s better than the static typing in most other languages because generics are decent and the inference is pretty reasonable. For example it seems better thought out than Java, C and (in my limited experience) Go. My rough feeling is that 75% of the Python ecosystem either has type annotations or has type stubs in typeshed. Where something particularly important is untyped, I tend to just wrap it and give it an explicit annotation (this is fairly rare). I’ve written some tips on getting mypy working well on bigger projects.

                                                                          I don’t think you have the right intuition that asyncio would help you if your problem is speed. I pretty convinced that asyncio is in fact slower than normal Python in most cases (and am currently writing another blogpost about that - UWSGI is for sure the fastest and most robust way to run a python webservice). Asyncio stuff tends to fail in weird ways under load. I also think asyncio is a big problem for correctness - it actually seems quite hard to get asyncio programs right and there are a lot of footguns around.

                                                                          Re: SQLAlchemy - I’m also very surprised. I think SQLAlchemy is a good ORM and I’ve used postgres specific features (arrays, json, user defined functions, etc) from it a great deal. If you want to write SQL-level code there is nothing stopping you from using the “core” layer rather than the “ORM” layer. There’s also nothing stopping you using SQL strings with the parameterisation, ie "select col_a from table where col_b = :something - I do that sometimes too. I have to say I have never had trouble with hand optimising a SQL query in SQLA - ever - because it gives you direct control over the query (this is even true at the ORM level). One problem I have run into is where people decide to use SQLA orm objects as their domain objects and…that doesn’t end happily.

                                                                          Celery however is something that I do think is quite limited. It’s really just a task queue. I am not sure that firing off background tasks as goroutines is a full replacement though as you typically need to handle errors, retry, record what happened, etc. I think even if you were using go every serious system ends up with a messaging subsystem inside it - at least for background tasks. People do not usually send emails from their webserving processes. Perhaps the libraries for this in go land are better but in Python I don’t think there is a library that gets this kind of thing wholly right. I am working on my own thing but it’s too early to recommend it to anyone (missive). I want to work on it more but childcare responsibilities are getting in the way! :)

                                                                          Best of luck in your rewrite/rework. I have not been impressed with GraphQL so far but I haven’t used the library you’re planning to use. My problems with GraphQL so far are that a) it isn’t amenable to many of the optimisations I want to do with it b) neither schema first nor code first really work that well and c) it’s query language is much more limited than it looks - much less expressive than I would like. You may not find that the grass is greener!

                                                                          1. 5

                                                                            I don’t think you have the right intuition that asyncio would help you if your problem is speed.

                                                                            I don’t want asyncio for speed, I want it for a better organizational model of handling the various needs of the application concurrently. With Flask, it’s request in, request out, and that’s all you get. I would hope that asyncio would improve the ability to handle long-running requests while still servicing fast requests, and also somewhat mitigate the need for Celery. But still, I’ve more or less resigned from Python at this point, so it’s a moot point.

                                                                            I am not sure that firing off background tasks as goroutines is a full replacement though as you typically need to handle errors, retry, record what happened, etc.

                                                                            Agreed. This is not completely thought-out yet, and I don’t expect the solution to be as straightforward as fire-and-forget.

                                                                            My problems with GraphQL so far are that a) it isn’t amenable to many of the optimisations I want to do with it b) neither schema first nor code first really work that well and c) it’s query language is much more limited than it looks - much less expressive than I would like.

                                                                            I have encountered and evaluated all of the same problems, and still decided to use GraphQL. I am satisfied with the solutions to (a) and (b) presented by the library I chose, and I feel comfortable building a good API within the constraints of (c). Cheers!

                                                                          2. 3

                                                                            So do you plan to keep the web UI in Python using Flask, and have it talk to a Go-based GraphQL API server? Or do you plan to eventually rewrite the web UI in Go as well? If the latter, is there a particular Go web framework or set of libraries that you like, or just the standard library?

                                                                            1. 4

                                                                              To be determined. The problems of Python and Flask become much less severe if it’s a frontend for GraphQL, and it will be less work to adapt them as such. I intend to conduct more research to see if this path is wise, and also probably do an experiment with a new Golang-based implementation. I am not sure how that would look, yet, either.

                                                                              It’s also possible that both may happen, that we do a quick overhaul of the Python code to talk to GraphQL instead of SQL, and then over time do another incremental rewrite into another language.

                                                                            2. 3

                                                                              I’m curious about why you consider that Flask does “a little bit too much”. It’s a very lightweight framework, and the only “batteries included” thing I can think of is the usage of Jinja for template rendering. But if I’m not wrong, sourcehut uses it a lot so I don’t thing this is what annoys you.

                                                                              Regarding SQLAlchemy, I totally agree with you. It’s a bad database abstraction layer. When you try to make simple queries it becomes cumbersome because of SQLAlchemy’s supposed low level abstractions. But when you want to make a fine-grained query it’s also a real pain and you end up writing raw SQL because it’s easier. In some cases you can embed some raw SQL fragment inside the ORM query, but it is often not the case (for example, here is a crappy piece of code I’m partially responsible of). Not having a decent framework-agnostic ORM is the only thing that makes me miss Django :(

                                                                              1. 8

                                                                                Regarding Flask, I recently saw Daniel Stone give a talk wherein he reflected on the success of wlroots compared to the relative failure of libweston, and chalked it up to the difference between a toolkit and a midlayer, where wlroots is the former. Flask is a midlayer. It does its thing, and provides you a little place to nestle your application into. But, if you want to change any of its behavior - routing, session storage, and so on - you’re plugging into the rails its laid down for you. A toolkit approach would instead have the programmer always be in control, and reach for the tools it needs - routing, templating, session management, and so on - as they need them.

                                                                                1. 1

                                                                                  I’ve personally found falcon a bit nicer to work with than flask, as an api/component.
                                                                                  That said, as a daily user for some mid-sized codebases (some 56k odd lines of code), I very much agree with what you said about python and sqlalchemy.

                                                                                2. 4

                                                                                  I find that linked piece of code perplexing because converting that from string-concat-based dynamic SQL into SQLA core looks straightforward: pull out the subqueries, turn them into python level variables and then join it all up in a single big query at the end. That would also save you from having a switch for sqlite in the middle of it - SQLA core would handle that.

                                                                                3. 1

                                                                                  SQLAlchemy: this is now my least favorite dependency in our entire stack. It’s… so bad

                                                                                  That’s also the only thing I remember about it from when I used it years ago. Maybe it’s something everyone has to go through once to figure out the extra layer might look tasty, but in the end it only gives you stomach ages.

                                                                                4. 13

                                                                                  Yeah, I’d be very interested to hear more about that too. Not that I disagree, but I think his article was light on details. What were the things that “soured” his view of Python for larger projects, and why was he “unsatisfied with the results” of REST?

                                                                                  1. 11

                                                                                    I found REST difficult to build a consistent representation of our services with, and it does a poor job of representing the relationship between resources. After all, GraphQL describes a graph, but REST describes a tree. GraphQL also benefits a lot from static typing and an explicit schema defined in advance.

                                                                                    Also, our new codebase for GraphQL utilizes the database more efficiently, which is the main bottleneck in the previous implementation. We could apply similar techniques, but it would require lots of refactoring and SQLAlchemy only ever gets in the way.

                                                                                  2. 1

                                                                                    Ive been using Flask and Gunicorn. I basically do native dev before porting it to web app. My native apps are heavily decomposed into functions. One thing that’s weird is they break when I use them in web setup. The functions will be defined before “@app” or whatever it is like in a native app. Then, Gunicorn or Flask tells me the function is undefined or doesn’t exist.

                                                                                    I don’t know why that happens. It made me un-decompose those apps to just dump all the code in the main function. Also, I try to do everything I can outside the web app with it just using a database or something. My Flask apps have stayed tiny and working but probably nearing the limit on that.

                                                                                  1. 1

                                                                                    https://theoldreader.com/

                                                                                    Never had any problems with them.

                                                                                    1. 4

                                                                                      So I am going insane. I swear that the original version of POV-Ray or its predecessor DKBTrace used “hither” and “yon” for what is now called “camera” and “look_at”. I remember reading that back in the early 90’s and thinking those terms were hilarious and well-chosen and that usage cemented my usage of those words in similar not-so-serious contexts.

                                                                                      (This is distinct from the concept of “hither and yon clipping.”)

                                                                                      I cannot find any evidence that this was ever true. I’ve gone to Aminet and downloaded the 1991 version of DKBTrace, versions of POV-Ray from 1.0 up to the early 2000’s…no mention of those words whatsoever. I’ve searched old Usenet postings.

                                                                                      Someone help me out here, did I dream it?

                                                                                      (I also remember some public acrimony in the early days of POV-Ray where there was some sort of trademark dispute or something and some new challenger appeared on the Amiga text-driven 3D raytracing scene based on older versions of POV-Ray or something…my memory is fuzzy, since this is like 25 years ago…)

                                                                                      1. 5

                                                                                        You sure you’re not thinking of polyray, not POV-Ray? It’s been forever, but I think that one did indeed use hither and yon, the syntaxes were very similar, and so are the names.

                                                                                        Edit: Bingo. And note that the author of Polyray contributed to POV-Ray, so there may even have been a very early version that did use both. http://paulbourke.net/dataformats/polyray/

                                                                                        1. 2

                                                                                          Thank you! It’s possible. Polyray was never released on the Amiga, looks like, so it would have to be some old pre-1.0 version of POV-Ray that was on the Amiga but had Polyray keywords. So far this is looking like the best option, I think.

                                                                                        2. 3

                                                                                          Spooky. Google shows that the book Physically Based Rendering: From Theory to Implementation from 2010 used these terms. Did you read that book?

                                                                                          1. 2

                                                                                            Thank you for looking! “Hither” and “yon” are standard-ish terms in 3D graphics for defining clipping planes and camera position and stuff.

                                                                                            But I remember POV-Ray supporting these terms as keywords specifically. I remember the documentation saying something like:

                                                                                            set the camera position using camera<0,0,0> and where it's looking with look_at<0,0,0>.
                                                                                            You can use the older hither<0,0,0> and yon<0,0,0> keywords if you'd like
                                                                                            

                                                                                            Something like that. They were supported as keywords in the POV-Ray language. I’m 99.9% sure.

                                                                                            1. 2

                                                                                              You should be looking on povray repository

                                                                                              1. 1

                                                                                                Thank you, but I think that’s not it I don’t think. Those appear to be talking about hither/yon clipping, which is something else and it doesn’t look like they were ever keywords in the language, just names of functions. It’s also the wrong year, from 2003 at the earliest (when OpenEXR was announced).

                                                                                                So thank you, but I don’t think that’s it, unfortunately.

                                                                                          2. 2

                                                                                            Mandela effect is a pain. My little two cents in the topic: back in the late 90s I spent some time playing around with POV-Ray and other open source raytracers and I don’t recall seeing those keywords used in any of the scene scripts I read.

                                                                                            1. 2

                                                                                              I swear that the original version of POV-Ray or its predecessor DKBTrace used “hither” and “yon” for what is now called “camera” and “look_at”.

                                                                                              Could it be that you wrote a ray tracer by hand and you chose the identifier names hither and yon in your own ray tracer?

                                                                                              1. 1

                                                                                                Hah, that would be awesome but sadly no. I never wrote a ray tracer.

                                                                                                (I am going to eventually do the Ray Tracer Challenge in my copious free time, though…)

                                                                                                1. 2

                                                                                                  Try one! A very basic one is really rather easy, and it’s very satisfying to get some real images out of a handful of math.

                                                                                              2. 2

                                                                                                Admittedly, it was a long time ago, but I don’t recall this being documented in version 2.0, the first one I’ve used. I just tried it under Dosbox and I can’t get POV 1.0, the oldest version still available on the website, to play ball with me if I use hither, nor do any of the binaries contain this string (albeit this doesn’t mean much). Maybe this was supported in some version, but not documented?

                                                                                                This is extraordinarily spooky because, now that you mention it, I could swear I remember it as well!

                                                                                              1. 4

                                                                                                The code is not in a hot path, and micro-optimizing it is not needed. But still wanted to know what’s faster.

                                                                                                I should have stopped reading right there :)

                                                                                                And it resulted in a Go patch of rather dubious real-world use, as noted by the commenters on that PR.

                                                                                                1. 4

                                                                                                  The part that I found compelling (and the reason I posted it here) was the description of the process that they went through (as a first-time contributor to the compiler) for understanding how the compiler generates optimizations and building a new one.

                                                                                                  1. 2

                                                                                                    True! That’s indeed the good part.

                                                                                                1. 3

                                                                                                  DuckDuckGo is the way to go for this very feature.

                                                                                                  1. 2

                                                                                                    I’m personally well aware of DDG and use it mostly, this happened to be on my kind of new iPhone where I haven’t changed the defaults yet.

                                                                                                    1. 2

                                                                                                      there is a whole where

                                                                                                      *hole

                                                                                                      :)

                                                                                                      1. 1

                                                                                                        Thanks, fixed :-)

                                                                                                  1. 2

                                                                                                    Great news! Also, surprised to see the amount of people here that don’t have the option of buying a laptop without an OS…

                                                                                                    1. 3

                                                                                                      There aren’t many OEMs that will sell you consumer laptops without Windows on them.

                                                                                                      1. 3

                                                                                                        I have one from Tuxedo which is… fine. (which looks like it’s the same whitelabel machines as system76 sells).

                                                                                                        1. 1

                                                                                                          I was just looking at a Tuxedo this week. I’m trying to talk myself into spending some serious money on a new laptop, and while I’d love a ThinkPad, giving some love to companies pushing linux seems great. Maybe I’ll wait to see what comes out of this Lenovo-RedHat story.

                                                                                                          1. 1

                                                                                                            Well I’ve just drooled over the P53 again. I think I will start selling out hardware around the house to collect some money for this, when it becomes available.

                                                                                                    1. 59

                                                                                                      Time.

                                                                                                      Take the time to take a step back, think about what you’re doing, and why. Don’t assign the PR you finished right away, but give it some time, let it linger, and see if later you can make it simpler. Don’t pick the first solution you came up with, but take some time to think about other options. In my experience, any other optimizations you make dwarf in comparison.

                                                                                                      1. 17

                                                                                                        Don’t assign the PR you finished right away, but give it some time, let it linger, and see if later you can make it simpler.

                                                                                                        Reviewing a PR yourself before assigning a reviewer can save you so much time and embarrassment.

                                                                                                        1. 5

                                                                                                          Alternatively, set up a work in progress PR as soon as you branch, so that you can get incremental feedback in small bites along the way. It’s way easier to deal with than a deluge of comments at the end of the process.

                                                                                                        2. 3

                                                                                                          Yes! The most powerful time for me is right before falling asleep. Last night, I came up with the perfect phrasing for an email I was having trouble writing and a solution for creating a moveable player in my game with a cellular automaton. It’s practically magic what a little background processing lets you achieve.

                                                                                                        1. 6

                                                                                                          Ok, trying to follow along that little getElementById section, I totally see where people are coming from that say Purescript documentation is terrible.

                                                                                                          1. getElementById has no documentation at all besides the types. Ok, I know the JavaScript version, but what’s NonElementParentNode? Clicking the type doesn’t help because actually we’re already on the page with the type definition. Also zero explanation. How do we make a NonElementParentNode?
                                                                                                          2. Ok, let’s cheat, the article spoiled that this argument might be document (does it have to be? how does getElementById actually use this argument?). So we find Web.DOM.Document in the same package. Oh look, some documentation text! And if we scan through the list of undocumented functions, we can tell that, yes, a Document gives us a NonElementParentNode via toNonElementParentNode.
                                                                                                          3. Alright, following that text at the start of the Web.DOM.Document, let’s look up the package purescript-web-html and look for the document. No meaningful introductory documentation, and the main module Web.HTML is even worse with just a long list of re-exports.
                                                                                                          4. So lets scan the list of 90(!) modules for something relevant. Web.HTML.HTMLDocument sounds like it might help. Hmm, no, just another long list of boilerplate.
                                                                                                          5. So let’s scroll further through the module list. window is kind of like document, and there’s a plain Web.HTML.Window module. Oh look, we got lucky! If we have a Window, we can now get a HTMLDocument via document :: Window -> Effect HTMLDocument. But wait, we wanted a Document, not a HTMLDocument. Luckily, that list of boilerplate functions in Web.HTML.HTMLDocument comes with toDocument :: HTMLDocument -> Document.
                                                                                                          6. We still need a Window, though, and Web.HTML.Window doesn’t seem to give us one. At this point I’m lost for a couple minutes, randomly click back to the top-level Web.HTML, and see that besides all the re-export boilerplate, hiding in plain site we have window :: Effect Window!
                                                                                                          1. 2

                                                                                                            Sounds like my experience with purescript. Also don’t forget to you first have to pick your framework, and then find a version which actually works with the purescript version you want to use.

                                                                                                          1. 6

                                                                                                            vanilla vim in a basic terminal. Works everywhere, and I don’t have to spend brain-cycles thinking about it.

                                                                                                            1. 2

                                                                                                              How do you deal with multi language barrier? ie. Space vs Tabs in different project? Do you manually expandtab/tabstop?

                                                                                                              1. 2

                                                                                                                Personally, until I need to do it often enough to put in my .vimrc for a particular filetype, I kinda already remember the :setl sw=2 ts=2 et (replace 2 with whatever needed) “magic incantation” for when needed. Umm… a moment of self-reflection: is vim really a stockholm-syndrome lover I thought it to be in my younger days?… Yet in other editors, I’d have to do it by mouse or keyboard shortcuts anyway…

                                                                                                                1. 1

                                                                                                                  I have an EditorConfig plugin, many projects ship with an .editorconfig file these days.

                                                                                                                  I also have some autocmd for specific languages/projects; you can just match it by directory:

                                                                                                                  au FileType go nnoremap MM :silent! :wa<CR>:compiler go<CR>:silent make!<CR>:redraw!<CR>
                                                                                                                  au FileType go nnoremap TT :silent! :wa<CR>:compiler gotest<CR>:silent make!<CR>:redraw!<CR>
                                                                                                                  
                                                                                                                  autocmd BufReadPre /home/martin/code/goatcounter/*.go
                                                                                                                              \  if $CGO_ENABLED is# '' | let $CGO_ENABLED = 0 | endif
                                                                                                                              \| let g:gopher_install_package = 'zgo.at/goatcounter/cmd/goatcounter'
                                                                                                                  

                                                                                                                  You can set tabs/spaces/etc. there too, if required.

                                                                                                                  1. 2

                                                                                                                    I think “vanilla vim” was important context for the question. With custom config, these things become easy, as you point out.

                                                                                                                    1. 1

                                                                                                                      Ah right; I assumed “vanilla” meant “without plugins”, but yeah, could be “without config” too 😅

                                                                                                              1. 1

                                                                                                                Why even try to reach remote software repos though, you can host everything locally, with your super simple customized protocol.

                                                                                                                1. 1

                                                                                                                  You have to get the software first to mirror it locally, unless you plan to create everything from scratch.

                                                                                                                  1. 1

                                                                                                                    customized

                                                                                                                    tftp should work

                                                                                                                  1. 7

                                                                                                                    To anyone else wanting to move away from Elm:

                                                                                                                    I recommend looking into PureScript as an alternative. It’s a much more capable functional programming language, akin to Haskell for the web, and the community is full of mature, respectful adults.

                                                                                                                    Learn more: https://www.purescript.org/

                                                                                                                    Discourse: https://discourse.purescript.org/

                                                                                                                    1. 4

                                                                                                                      I tried Purescript about a year ago, as an Elm replacement. It’s indeed very Haskell-y, but I found documentation and working(!) examples extremely lacking. I gave up after days of fighting to get a working prototype.

                                                                                                                      I recently use nim to do some simple web stuff, and that was maybe not as great, but it was easy for me to get something working. Hours compared to days in purescript.

                                                                                                                      That’s my experience. I have very, very little patience for over-abstracted code and obtuse libraries in general, though.

                                                                                                                      1. 2

                                                                                                                        Mint might be nice alternative as well (when it reaches 1.0 of course). Written by person who moved away from Elm. Less Haskell-y than Purescript/Elm.

                                                                                                                        https://www.mint-lang.com/

                                                                                                                      1. 26

                                                                                                                        Don’t forget to read the ‘All That Said…’ at the end. It’s likely the most important advice in this whole list.

                                                                                                                        1. 11

                                                                                                                          Yeah, I wish this article had a better title since it was a good read and clearly written by someone with experience. The article (+ the medium.com domain) made me think it was going to be clickbait though.

                                                                                                                          1. 4

                                                                                                                            I thought it was going to be like the titular list in “10 Things I Hate About You” and the final point would be something like “I hate that I love you so much” or something like that.

                                                                                                                            I guess it’s pretty close, though.

                                                                                                                          2. 1

                                                                                                                            This. I really wish that had been at the top, because some of these are pretty deep dives/issues at scale, and many people may not get to the end (especially if there’s an Oracle salesperson calling frequently).

                                                                                                                          1. 1

                                                                                                                            Good idea to get a 4K monitor. I recently upgraded as well and terminals are so much nicer to read.

                                                                                                                            I got a LG 27UL850. Works fine for me, and it isn’t much bigger than the 23(?) inch Dell it replaces, just a bit wider. Didn’t try the USB-C yet. No problems with Linux.

                                                                                                                            1. 5

                                                                                                                              Have you read Jean Sammet’s Programming Languages: History and Fundamentals?

                                                                                                                              It has a lot of great information on earlier languages.

                                                                                                                              Here are a couple of features that first showed up in Algol 68.

                                                                                                                              • pragmas
                                                                                                                              • operator definition

                                                                                                                              Sammet says that MAD is the origin of constants.

                                                                                                                              I wouldn’t say that PL/I is a better language than Algol 68; it has more than its share of quirks.
                                                                                                                              Here is an example from John Levine.

                                                                                                                              PL/I suffers from a lot of features that individually make sense but do silly things in combination. My favorite example is this one:

                                                                                                                              DCL (A,B,C) CHAR(3); A = ‘123’; B = ‘456’; C = A+B;

                                                                                                                              The value of C is three spaces, because the arithmetic result is converted to a default length string ’ 579’ and then truncated from the right.

                                                                                                                              1. 1

                                                                                                                                Have you read Jean Sammet’s Programming Languages: History and Fundamentals?

                                                                                                                                That’s a recommendation to go read it?

                                                                                                                                1. 2

                                                                                                                                  It’s a comprehensive survey of programming languages from the 1950s to 1969.
                                                                                                                                  It’s well written but I would read it only if you are quite interested in older programming languages.

                                                                                                                              1. 12

                                                                                                                                Doom Eternal and Animal Crossing

                                                                                                                                1. 5

                                                                                                                                  I’d play a mashup of those games.

                                                                                                                                  1. 3

                                                                                                                                    Doom Eternal runs perfectly. Rock-solid 60 FPS, it is a beautiful dance of combat and flight. It feels like Quake in the best way.

                                                                                                                                    Animal Crossing is a slow-burn game. It is not the fastest game in the world. It will probably take me a year or more to get through all of the content. However, this is exactly what I wanted.

                                                                                                                                    1. 2

                                                                                                                                      I’m still stuck trying to get to lvl 20 of Slay the Spire. It’s getting hard.

                                                                                                                                      1. 1

                                                                                                                                        What are you running it on? I don’t have ‘game level’ PC or Mac hardware and I don’t really fancy investing in more games for my XB1 as I never use it anymore. Also I doubt Doom will be as much fun without a mouse.

                                                                                                                                    2. 2

                                                                                                                                      A natural pairing if ever I heard one. :D

                                                                                                                                      1. 1

                                                                                                                                        Doom Eternal here as well. Played for about an hour last night (it unlocked rather late for my local time), and I’m really enjoying it so far.

                                                                                                                                      1. 14

                                                                                                                                        The hard lesson for me was that I earn my pay when customers I build for pay money. Sometimes that involves hard work, lots of expertise and clever code, but at best these things are necessary but not sufficient.

                                                                                                                                        I’ve fixed tough issues and written beautifully-engineered code for startups which ultimately failed. And there were times when I’ve earned more than my salary with simple config changes, by saying “no”, or just by charging more.

                                                                                                                                        1. 7

                                                                                                                                          Isn’t that a mindbender? I think that’s why senior engineers get paid more–not necessarily because they can crank out more code, but because they can get you to the solution quicker (partially because of all the mistakes they’ve made in the past).

                                                                                                                                          1. 12

                                                                                                                                            You’re a senior developer IFF you crank out less code. Writing code is easy, figuring out how to not write code is where things get fun.

                                                                                                                                            1. 8

                                                                                                                                              Yup. The best coder in the world comes in, listens to your problem, asks questions, and then says “You don’t need any code at all. Do this.” and leaves. That’s a person that both knows what the tools are for and when it’s appropriate to use each one. The worst coder in the world comes in, asks a question or two, and then fires up what language and tooling system either they use all of the time or have been dying to try out, then disappears, only to reappear 50KLOC of code and framework later.

                                                                                                                                              It’s a strange concept. We don’t pay doctors to act this way. Or lawyers. Or any professional, for that matter. Yet it is the norm in most of the coding world.

                                                                                                                                              1. 2

                                                                                                                                                But we do. Doctors will tell you when a surgical intervention is not necessary. Lawyers will tell you when to settle or drop a case.

                                                                                                                                                1. 2

                                                                                                                                                  It’s a strange concept. We don’t pay doctors to act this way. Or lawyers. Or any professional, for that matter. Yet it is the norm in most of the coding world.

                                                                                                                                                  Maybe because software is so abstract? Not sure why..

                                                                                                                                                  1. 2

                                                                                                                                                    I think there are a lot of reasons. The best short answer I have is that it’s because programmers enjoy creating complex puzzles that they can then master. It makes them happy. And so, over time, they make their own and make generalized versions that other programmers can master too. We do what makes us happy.

                                                                                                                                              2. 6

                                                                                                                                                Senior people get paid more partly because the decisions they make shape spending for months and years to come, and partly for the experience you mention.

                                                                                                                                                1. 2

                                                                                                                                                  I actually have an opposite experience. Senior people in my company (more senior than me) actually slow down getting to the solution, mostly by insisting on the documentation, processes, defining OKRs in a beautiful and measurable way. That seems quite ok for a company that has stable directions and billions in bank, but for a startup that is a killer (ihmo).

                                                                                                                                                  1. 4

                                                                                                                                                    Depends on the person and the job, it sounds like. I work in a place that is in the process of going from a startup to a company that needs more of the documentation and processes. We have some excellent senior engineers who have been there since it was a small startup, they are fantastic problem-solvers, and getting them to commit to stuff more far-reaching than “it works for the next demo” is like pulling teeth.

                                                                                                                                                    1. 5

                                                                                                                                                      Absolutely true. The seniority goes hand in hand with past experiences. Startups generally need stuff that works, discovery and documentation is left as less important. In big companies is the other way round. But both optimize for the most important items first.