1. 26

    I’m a little bit suspicious of this plan. You specifically call out that you already have an anti-OOP bias to the point of even saying “no true Scotsman” and then say you plan to take anything someone sends you and denigrate it. Since no codebase is perfect and every practitioner’s understanding is always evolving, there will of course be something bad you can say especially if predisposed to do so.

    If you actually want to learn what OOP is like, why not pick up a known-good text on the subject, such as /99 Bottles of OOP/?

    1. 9

      I, for one, think this project seems very interesting. The author is correct that criticisms of OOP is often dismissed by saying “that’s just a problem if you don’t do OOP correctly”. Naturally, a response is to ask for an example of a project which does OOP “correctly”, and see if the common critiques still applies to that.

      Maybe the resulting article will be uninteresting. But I think I would love to see an article which dissects a particular well-written code-base, discusses exactly where and how it encounters issues which seem to be inherent in the paradigm or approach it’s using, and how it solves or works around or avoids those issues. I just hope the resulting article is actually fair and not just a rant about OOP bad.

      EDIT: And to be clear, just because there seems to be some confusion: The author isn’t saying that examples of good OOP aren’t “real OOP”. The author is saying that critiques of OOP is dismissed by saying “that’s not real OOP”. The author is complaining about other people who use the no true Scotsman fallacy.

      1. 6

        Explicitly excluding frameworks seems to be a bit prejudiced, since producing abstractions that encourage reuse is where OOP really shines. OpenStep, for example, is an absolutely beautiful API that is a joy to use and encourages you to write very small amounts of code (some of the abstractions are a bit dated now, but remember it was designed in a world where high-end workstations had 8-16 MiB of RAM). Individual programs written against it don’t show the benefits.

        1.  

          Want to second OpenStep here, essentially Cocoa before they started with the ViewController nonsense.

          Also, from Smalltalk, at least the Collection classes and the Magnitude hierarchy.

          And yes, explicitly excluding frameworks is non-sensical. “I want examples of good OO code, excluding the things OO is good at”.

        2. 2

          Maybe the resulting article will be uninteresting. But I think I would love to see an article which dissects a particular well-written code-base, discusses exactly where and how it encounters issues which seem to be inherent in the paradigm or approach it’s using, and how it solves or works around or avoids those issues. I just hope the resulting article is actually fair and not just a rant about OOP bad.

          That’s exactly my plan. I have my biases and existing ideas, but I’ll try to keep it open minded and maybe through talking about concrete examples I will learn something, refine my arguments, or just have a useful conversation.

          1. 2

            The author is correct that criticisms of OOP is often dismissed by saying “that’s just a problem if you don’t do OOP correctly”.

            I know this may look like splitting hairs, but while “that’s only a problem if you don’t do OOP correctly” would be No True Scotsman and invalid, what I see more often is “that’s a problem and that’s why you should do OOP instead of just labelling random code full of if statements as ‘OOP’” which is a nomenclature argument to be sure, but in my view differs from No True Scotman in that it’s not a generalization but an attempt to make a call towards a different way.

            I agree that a properly unbiased tear-down of a good OOP project by someone familiar with OOP but without stars in their eyes could be interesting, my comment was based on the tone of the OP and a sinking feeling that that is not what we would get here.

            1.  

              OOP simplifies real objects and properties to make abstraction approachable for developers, and the trade-off is accuracy (correctness) for simplicity.

              So if you would try to describe the real world in OOP terms adequately, then an application will be as complex as the world itself.

              This makes me think that proper OOP is unattainable in principle, with the only exception – the world itself.

            2. 3

              One could argue in favor of Assembly and still be right, which doesn’t make “everyone program should be written in Assembly” a good statement. It sounds, to me, like saying “English will never have great literature”. It doesn’t make much sense.

              Microsoft has great materials on Object-Oriented-Design tangled inside their documentation of .NET, Tackle Business Complexity in a Microservice with DDD and CQRS Patterns is a good example of what you want, but is not a repository I am afraid. Design Patterns has great examples of real world code, they are old (drawing scrollbars) but they are great Object-Oriented-Programming examples.

              Good code is good, no matter the paradigm or language. In my experience, developers lack understanding the abstractions they are using (HTTP, IO, Serialization, Patterns, Architecture etc.) and that shows on their code. Their code doesn’t communicate a solution very well because they doesn’t understand it themselves.

              1. 3

                you plan to take anything someone sends you and denigrate it.

                I could do that, but then it wouldn’t be really useful and convincing.

                If you actually want to learn what OOP is like, why not pick up a known-good text on the subject, such as /99 Bottles of OOP/

                Because no real program looks like this.

              1. 4

                After driving with GPS, I have quickly degraded my natural ability to navigate. This is when I have decided to switch from an intelligent IDE to vim.

                1. 3

                  After driving with a GPS that gets confused between left and right, I’ve lost my ability to feel confident in where I’m going. So I decided to pull out my vim setup to try and regain that confidence.

                  1. 3

                    More seriously, I’ve found one of the easiest solution to get some of that semblance of navigation back is to configure your application/device to always point north. It makes you more aware of your absolute position and alternate routes that will take you to the same spot.

                    I wonder what the IDE analogy is for this.

                    1. 1

                      Keep the high-end IDE in the toolbox, even if it isn’t your daily driver. The code generation and refactoring tools in Intelij let you perform miracles on the odd occasion they are needed. Honestly most people who use IDEs for everything don’t know how to use those tools.

                    1. 8

                      I can only say that it works so well that I never needed to understand it deeper. My biggest chore related to dependencies is to clean go.sum files from time to time. I use go mod tidy for that.

                      I also run local go modules proxy (Athena) at home simply because I often update dependencies in multiple projects.

                      1. 6

                        My biggest chore related to dependencies is to clean go.sum files from time to time. I use go mod tidy for that.

                        You do not need to clean go.sum. That file is an (almost) append-only log of checksums of modules that may be considered when building your dependency graph, and it’s used only to detect corruption or malfeasance. It’s not a lock file, and it doesn’t really represent the size or state of your dependency graph. (At best, it has a correlative relationship.) You should check it in for safety reasons, but there is basically no reason you should ever look at it, or care how big it is.

                        1. 1

                          Thanks, it is good to know! I believe it is just a personality deformation, to delete lines that are not required.

                          1. 2

                            I don’t think “required” is actually well-defined, when it comes to lines in go.sum.

                            1. 2

                              In a situation when you were using foo@v1.0.0 and then updated to v1.0.1. The record in go.sum related to the older version does not serve any purpose. Is this a valid assumption?

                              1. 1

                                Well, does a transitive dependency depend on v1.0.0? Depending on how optimized the modules tool is, it could still need to download and evaluate it.

                                1. 2

                                  I see this makes sense, and my understanding was the same.

                                  I have slowly navigated towards a minimal possible number of dependencies in my code. Bumping a single dependency typically (but not constantly) leaves me with just one version required.

                          2. 1

                            IIRC go.sum should only “shrink” when you switch or remove a dependency, right?

                            1. 2

                              I don’t think you can make very many authoritative claims about the relationship between your dependency graph and the contents of go.sum. The rules that govern that relationship change from version to version.

                          3. 5

                            I can only say that it works so well that I never needed to understand it deeper

                            Yup. I remember fiddling about with packages in Node and Python so much, but never really had to worry in Go.

                            With Go, there was the initial occasional issue with people changing the capitalisation of their usernames (and causing package breakages), or initial teething issues, but when everyone follows the rules (and most do), it works fine!

                          1. 6

                            I wish this included some explanation of why these are useful or explanations of why they are. Like, why use zerolog over logrus/zap/whatever? Why use gorilla/mux over gin or go-chi or the standard library? Why have that overly-complex unique code thing to determine where a logged error is thrown instead of making sure your errors have stack traces?

                            1. 4

                              I’m not the author of the post, but I can try to answer some questions:

                              Like, why use zerolog over logrus/zap/whatever?

                              zerolog and zap is what the author of logrus admit he would write if he wrote logrus/v2. zerolog claims to be a “better” version of zap (there are claims of performance improvement on other people’s computers)

                              Why use gorilla/mux over gin or go-chi or the standard library?

                              gorilla/mux is much lightweight as opposed to gin which has renderers, etc… Also, there are claims of very high performance on other people’s computers. With gorlla/mux you can plug your own json de-/serialization library which is faster than gin’s.

                              Why have that overly-complex unique code thing to determine where a logged error is thrown instead of making sure your errors have stack traces?

                              ¯\(ツ)/¯ No idea… The unique code approach feels very hacky and brittle to me.

                              1. 3

                                I would even suggest “httprouter” as a more lightweight alternative to gorilla/mux.

                                1. 2

                                  Most “higher level” Go routers are probably based on it anyways.

                                2. 1

                                  We use unique logging codes for our logs, and it makes it easier to locate a particular statement in either the logs, or the source code. Now, we generate the unique code by hand, but it’s not a horrendous issue for us (we have a separate document that records each logging statement).

                                3. 1

                                  I’d be curious to know the best practices for annotating errors with stack traces. Any good library suggestions?

                                  1. 3

                                    github.com/pkg/errors has some nice helpers, as described in its documentation. The issue is that it requires changing your code to using errors.Errorf() instead of fmt.Errorf().

                                    Go2 will do it differently, but natively

                                    1. 3

                                      Also, errors.Wrapf is very useful when handling errors from packages which don’t use errors.

                                      1. 2

                                        You can wrap errors using fmt.Errorf() and the %w verb, and then later deal with them using errors.Is() or errors.As() and friends.

                                        1. 1

                                          That doesn’t give you stack traces though, correct?

                                      2. 2

                                        YES. This is probably my biggest issue with supporting production Go code. Errors are still too hard to locate.

                                        1. 1

                                          I personally use this to manage all that. If you use the errors.Wrap() function to wrap errors, it’ll add stack traces. And if you want to make a new error, the errors.New() function will also add a stack track where it was called. Then when you’re logging the error make sure it’s being logged in a way that will not just print the message. Most loggers should have a way to do that (I know zerolog and logrus do).

                                          1. 1

                                            Shameless plug for my errors library: https://pkg.go.dev/github.com/zeebo/errs/v2

                                            • captures stack traces, but only once even if wrapped multiple times
                                            • plays nicely with standard library errors package with errors.Is and errors.As
                                            • has a “tag” feature to let you associate and query tags with errors
                                            • helper to keep track of groups of errors
                                        1. 9

                                          My experience is that pure Go tests cover 99.9% of cases. Testify feels like an inability to adapt to a new language.

                                          1. 20

                                            My experience is that pure Go tests are obnoxious to write and debug. You have to tediously format debugging information yourself every time instead of having assert functions do that for you. That in turn makes failing tests harder to debug when the person before you didn’t bother doing it, or did so inconsistently.

                                            Worse, I’ve noticed that Go tests tend to assert the final results, but little in between. My tests usually assert every step of the way so I don’t have to spend time figuring out what went wrong. Perhaps if Go tests had a convenient assert, people would assert more.

                                            Asserts aren’t just for tests either. I use debug asserts all over my code. Asserts catch bad assumptions early, document those assumptions, and maintain their own freshness (they’ll fail when they’re wrong, unlike comments).

                                            1. 6

                                              Interesting, I do test intermediate state, but mostly errors.

                                              Formatting never was an issue for me. Sometimes comparing complex structs may be tedious. If reflect.DeepEqual does not help, than I use github.com/google/go-cmp and that is it.

                                              Other than that I do not have any issues with the testing package.

                                              1. 4

                                                When a test is failing, do you ever printf debug? At some point I noticed that the majority of the time I add prints (or check values in a debugger), I’m looking for something specific that indicates the value is right or wrong. Asserts work perfectly for that, in tests or normal code.

                                                1. 1

                                                  Most of the time failed test gives me enough information about the underlying issue, maybe because the scope of unit tests is somewhat limited.

                                                  If a test fails and I do not know the answer, I instrument my production code with a bunch of fmt.Printf statements, and it helps to find the issue.

                                                  I prefer limited output when it comes to debugging rather than overly verbose.

                                                  I see your point. I would imagine we have different coding styles.

                                              2. 6

                                                I use https://github.com/google/go-cmp and the standard library testing package. I like the combo.

                                            1. 2

                                              I’ve been using a kanban-style workflow with a Trello board for the past few years, and it’s been pretty life-changing. It’s hard to remember how I used to get stuff done beforehand, in the dark days of a brief list scribbled on my whiteboard. I’ve completed 1028 tasks since June 2018.

                                              It’s more of a to-do list than a goal tracker… but I guess I could handle large goals with a new column for epics, and linking other cards to those. Or just a new label.

                                              For some goals, like eating a few portions of fruit or vegetables every day, I use beeminder. A daily recurring Trello card would be way too much overhead. I only have three of those, though.

                                              1. 1

                                                I have started with a similar Kanban + Trello model. And just recently, I have moved to my own simplified “Get Things Done” solution. I have analyzed what parts of Trello I am using, and it turns out not many. So I have written my small tool using vanilla JS and Go.

                                                Concepts:

                                                • Each to-do item is a Project;
                                                • Each project consists of Actions;
                                                • I always split Project into the most simple atomic Actions;
                                                • “Drafts” is for something that is not refined yet;
                                                • “Active” is what I am going to do next, sorted by priority;
                                                • “Next Actions” is a unique column that contains Actions from “Active” projects. The column gives me a list of simple things to do that moves me towards my goals;
                                                • “Waiting” is for tracking delegated Actions;
                                                • “Maybe” is for something that I want to do in the future, but it is not a priority. It is nice to have such inspirational goals separately and not at the bottom of your current Projects.
                                                • “Done” is for completed items that I keep in this column for a week and then archive during the weekly board review;

                                                Columns:

                                                • Draft projects
                                                • Active projects
                                                • Next Actions & Waiting
                                                • Maybe / One day
                                                • Done

                                                Why it is good for me:

                                                • I dump every plan there and keep my head free from these thoughts;
                                                • I analyze and refine every Project while I am fresh. Then I just complete primitive minimal Actions one after another during the day;
                                                • I do not forget about household chores because I have both professional and personal projects on the board;
                                                • I am not trying to sell myself any kind of golden plans and perks to boost my app with features that I do not use;
                                                • It is not a universal Kanban board; contrary - it has a rigid structure, where I have one way of doing things;
                                                • I own my data;
                                                • I learned new things while implementing it;
                                                • I am the only customer, so this is 100% product-market fit.
                                                1. 1

                                                  Can I ask if you would dump this code under some permissive license? I’ve wanted something like this, and I promise not to ask for bug fixes

                                                  1. 1

                                                    UPD: @Vaelatern, I have deleted my first answer to your comment. I will try to provide more details in this reply.

                                                    I like the idea of open sourcing my tool. What currently prevents me from doing so is the storage backend. I am using Google Datastore, just because it was faster to do it this way. And I realize that it can be painful to deploy it now. I believe self-hosted version will do better with sqlite. I will implement it and then publish. Cheers.

                                                    1. 1

                                                      Lobste.rs replies are back! Have you decided not to do this, or is this still on your roadmap?

                                                      1. 2

                                                        Haha! Thanks for the reminder. I have successfully migrated the backend to SQLite. Some minor UI issues require a fix. But overall, I am ready to opensource it. BSD license.

                                                        I will write a short post about it 😁

                                                        1. 1

                                                          Huzzah!

                                                          1. 1

                                                            While I am working on release, you can check it here: https://getdone.club

                                              1. 13

                                                What the colleague was proposing sounds like premature abstraction. I see this quite often, mostly with junior and medior programmers but sometimes in seniors too (hell, I still catch myself doing it) - they start inventing all kinds of abstractions and patterns “in case this code needs to change”. But the thing is, you never know how the code will change. The things you make configurable might actually never change, and the things you baked in as assumptions might change several times.

                                                I find it’s best to write the code to be as straightforward as possible, so it’s easy to understand. Easy to understand code is easier to change too, because you’ll have an easier time figuring out the repercussions of your changes. Code that is factored in a way that makes it easy to understand and modify in various dimensions while still being efficient is the mark of a true craftsman.

                                                As you get more experienced, you’ll notice the kinds of things that tend to change a lot (data models, database layouts etc) and you’ll develop a style that favors “premature abstraction” for these kinds of things. If you’re lucky, your past experience affects the future work too and you’ll be right on the money with your abstractions. They were still a bit premature in a way, but because you’re working in a similar context you see the same patterns over and over and you and your colleague will thank your prescience that allowed the easy changes.

                                                However, be wary of carrying over these abstractions to other kinds of work, as your hard-won experience will actually betray you. For example, people working on frameworks and libraries tend to favor decoupling database code so that it can work on multiple databases. This is often the right thing to do for libraries, but in end-products it just makes code needlessly complex and slow - there it makes more sense to maximally leverage the features of the database you chose in the first place.

                                                1. 6

                                                  I agree with all sjamaan has written. Also, I want to add:

                                                  What the colleague was proposing sounds like premature abstraction.

                                                  I call that YAGNI.

                                                  I was going to joke by starting my comment as “I stopped reading at ‘My initial proposal was to add a class…’”. I was a heavy Python class user (inherited from University Java class, and the Python community), now that I have seen how much pain it is, and how much easier it is to work and debug with simple data types and functions (possibly passing function as arguments to do inversion of control).

                                                  I did not write “you do not need classes or disjoint types, and anything beyond dict and list is overkill”. My current approach is to start new code as simple as possible but not simpler. To be able to grasp that, I learned standard Scheme that does not have OOP builtin.

                                                  I quote the great last sentence of the previous comment that is my approach to build “abstractions” on top of SQL databases:

                                                  in end-products it just makes code needlessly complex and slow - [in end products] it makes more sense to maximally leverage the features of the database you chose in the first place.

                                                  1. 1

                                                    The story is slightly editorialized to (hopefully) by applicable to a larger audience. The actual language being used doesn’t even have classes. In general I agree that OOP is a tool to use sparingly.

                                                    1. 1

                                                      (opinion) OOP is a great tool for API discovery

                                                      I’ve not found it useful for much else tbh.

                                                  2. 4

                                                    I agree that what my colleague proposed was premature abstraction, at least from my perspective. But as you note, oftentimes experience will favor certain premature abstractions. This colleague was also at a high senior level (at least in title), so I like to give benefit of the doubt that they know what they are doing.

                                                    What is interesting is that “code as straightforward as possible” also suffers from ambiguity. From your comments, I believe you and I agree on what that looks like, but someone from a different background may completely disagree. My colleague might argue that their proposal was very straightforward! The absolutely fascinating bit is that “decoupled code” and “code that is simple” is something we all know as the goal, but the true definitions of what these mean are not actually defined.

                                                    I thought it was just so utterly strange that two entirely different approaches can both be justified with the same reasoning – and is there any way to “prove” that one approach is more correct than the other? Or are we just basing it all on our personal background experience?

                                                    1. 4

                                                      I don’t think you can “prove” that one approach is better/more correct than the other, because that’s highly subjective. Everybody will be bringing in different baggage. My gut says that “straightforwardness” as I call it should be measurable - the less indirections, classes, methods and conditionals you have, the more straightforward the code is.

                                                      But even this relatively objective measure can be perverted, I’m sure, because programmers are just so damn good at that. Just think “code golf” to reduce the LOC count - that doesn’t necessarily make it more readable/straightforward.

                                                      1. 3

                                                        I lean towards agreeing that “proving” one approach over the other is impossible. Then I guess the question is, if everyone has different, subjective, ideas of what “straightforward code” and “decoupled code” is, does it even make sense to have “straightforward and decoupled code” as the north star for software engineering? If none of us agree on where that north star is in the sky, we’re all going in different directions with entirely different maps of the world.

                                                        This is mostly just a philosophical question, one which I find myself considering. The engineer/scientist in me truly wants to define what good code looks like, but if its fundamentally a problem about people then that is clearly impossible.

                                                        1. 3

                                                          It’s a very good question indeed. I think maybe it’s much less important as to where your particular north star is than to have a consistent vision of where that star should be in a given team/project. That way, you get a consistent codebase that perhaps some onlookers find horrible and badly engineered, but everyone on the team agrees is a joy to maintain.

                                                          At least in my own experience, in a team where people are aligned on a certain style of engineering, you get a codebase that has an internal coherency. If different parts are done by people with different viewpoints, you get a Frankenstein monster that has no sense of identity. I’ve seen this happen, and it can be very difficult to reconcile this without somehow “enforcing” one view on those that disagree. And even talking about it can be difficult, because it’s something that’s very difficult to adequately put into words.

                                                          I think this is something that “style guides” try to do but fail at miserably because they’re only focused on the superficial syntactical details. But choosing (for example) whether to use interfaces or callbacks in a codebase goes much more deeply than that. And to make matters worse, there may be cases where there’s a good technical reason to deviate from the common convention.

                                                          1. 2

                                                            That is a good point, perhaps the only thing that does matter is what the team agrees on. Being aligned on the north star is certainly important, but where the north star is might not be. Definitely something I’m going to need to spend some time mulling over.

                                                            Then there is the question of whether a particular north star will “dominate” so to speak. For instance, complex abstractions are oftentimes difficult to replace while more straightforward approaches are oftentimes easier. The paradox is that straightforward code is usually refactored until it is complicated and difficult to change, while complicated code remains complicated. Does a team or project’s north star inevitably drift towards complicated over time? My instinct says yes, which I feel has some interesting consequences.

                                                            1. 1

                                                              Does a team or project’s north star inevitably drift towards complicated over time? My instinct says yes, which I feel has some interesting consequences.

                                                              hm, I’d have to agree that, in general, products tend to become more complicated due to the pressures from changing requirements, and the code follows suit. With one caveat: I’ve seen some refactorings that made needlessly complicated code much clearer (but possibly more abstract and complex to understand from scratch). Sometimes all it takes is some time away from the code to realise how it can be made simpler by finding the right abstraction for that particular part of the code. And sometimes it takes multiple iterations of refactorings in which one realises that eventually entire parts can simply be dropped.

                                                              1. 2

                                                                As an aside, I think the fact that products will grow more complex over time is a great reason not to start out making things complex right away. That complexity will probably be in different places than you expect, just like performance bottlenecks will be in different places than you expect. So premature abstraction is like premature optimization in that sense, and also in the sense that it is silly to design code in a way that you know will be slow/difficult to adapt to changing requirements - it just takes a lot of experience to see where to apply optimization and abstraction ahead of time.

                                                          2. 1

                                                            In the long term this can be measured in money: how much product earns and how much it costs to support it. Earnings incorporate values like customer satisfaction. Spends can tell about team velocity and maybe even employee churn.

                                                    1. 7

                                                      I can relate to this topic. Background: I have started with Java and later transitioned to Go. At this point, I have more Go experience than Java. I will concentrate on these two languages. But I believe that concerning the topic we can roughly compare two groups of languages: (Go, C, Rust, etc.) and (Java, .NET, Ruby, etc.).

                                                      I have noticed that traditions (or cargo-cult) in Java are solid. You always apply some layered architecture, you always use interfaces, and you always test against your mocks. Often you do not ask a lot of questions with well-established legacy corporate systems. And very often, your framework will dictate how to structure your code.

                                                      In Go, and I believe in C as well, things are more straightforward. Due to the niche (infrastructure in case of Go) that language occupies, in most cases, you have a minimal system that is fine-tuned to solve one particular problem, interacting with well-known external systems. In Go, you declare an interface only when you can not avoid having one. Overall approaches are much more practical, and this allows you to write simpler code. If there is no framework, you have more freedom. In Go, the standard library is your framework.

                                                      Sure, nowadays, we have more extensive Go applications that adopted some enterprise patterns. Still, an excellent project will strike you as a minimal sufficient implementation to solve concrete tasks.

                                                      Comparing my experiences with both approaches: you get a more reliable solution when you test against the real deal (DB, queue, fs, etc.) and avoid extensive use of mocks. You get a more straightforward and more readable solution when you use fewer abstractions. Simple code makes handling (rare) incidents at 3 AM way easier.

                                                      The enterprise approach gives you the flexibility to switch vendors rapidly, a requirement that can not be ignored in the real world.

                                                      There is a middle ground when abstraction and complexity are extracted into a separate library and maintained independently. Then you can have both: simplicity and flexibility. From my experience, “gocloud.dev” is an excellent example of such an approach. This set of libraries abstracts away different cloud implementations for you. You can keep your applications small and simple and still have an opportunity to switch vendors and perform tests.

                                                      To conclude, I want to say that collaboration between people with radically different views often makes the product better. It is hard, and staying respectful is the key (reminder to myself).

                                                      1. 4

                                                        Thanks for your thoughts! I think you capture the trade-offs between the two approaches well, and I agree, there do seem to be two groups of languages in this regard.

                                                        I have noticed that traditions (or cargo-cult) in Java are solid.

                                                        I have found this as well. Every colleague I have worked with that had an extensive Java background knew the name of every design pattern, and would describe all code and approaches as combinations of these. Personally, I’ve just understood the concepts behind the various design patterns, otherwise I just apply them where they fit naturally (only remembering the name of a tricky few which are handy for explaining to junior engineers so they can research them more). Completely different approaches!

                                                        To conclude, I want to say that collaboration between people with radically different views often makes the product better. It is hard, and staying respectful is the key (reminder to myself).

                                                        I couldn’t agree more. The combination of different views and backgrounds is incredibly important for a robust product. Staying respectful, identifying trade-offs, and justifying decisions (without cargo-culting in either direction) is extremely difficult and what I think is the true mark of an expert.

                                                      1. 27

                                                        I always suggest Go. If this is a project that will be “complete” soon, then you have good chances to release your code after years, compiled with newer versions of Go, without the need to rewrite it. (If you used standard library packages.)

                                                        Unfortunately, writing CRUD in Go can be tedious because there are no generics, but this is fine. Later, when you realize that your abstractions where wrong, you will thank Go for the lack of generics anyway.

                                                        1. 5

                                                          If this is a project that will be “complete” soon, then you have good chances to release your code after years, compiled with newer versions of Go, without the need to rewrite it. (If you used standard library packages.)

                                                          I’m sure this is more true today, but when I wrote a few Go projects several years ago, it sucked trying to compile them again on a new machine because the dependencies were all screwed up. I can’t believe Go went so long without good dependency management. Well, I guess I shouldn’t be surprised because there’s Python…

                                                          In any case, Go-the-language was pleasant to work with. I hope the recent work on dependency management has made it much smoother to actually compile an older project.

                                                          1. 2

                                                            Funny, I haven’t felt the pain of no generics when writing Go CRUD apps, but rather I’ve felt the pain of writing out a bunch of SQL queries and getting a nice “get all” endpoint for each object with filtering. I know ORMs are a thing, but I feel like they’re (almost) an anti-pattern in Go - at the very least I never see them recommended.

                                                            1. 2

                                                              sqlc is pretty great https://github.com/kyleconroy/sqlc

                                                            2. 2

                                                              JVM backwards compatibility is great too.

                                                            1. 1

                                                              I fail to see the use for Go for non-enterprise projects. It doesn’t seem to offer anything interesting compared to languages I already know (like C) apart from goroutines. I would love to be proved otherwise, though.

                                                              1. 11

                                                                I like using go for (personal) server applications, because it has a comprehensive and well-designed standard library, that generate static binaries that can be easily copied around. With the new embed directive, this should make it even easier. I think Go doesn’t want to be interesting, it wants to be convenient, and that is certainly something it manages.

                                                                1. 4

                                                                  I love to use Go for personal projects because it costs nothing to host it. I am talking about resources consumption. It fits on many “free plan” VMs, where Java would not.

                                                                2. 2

                                                                  Personally, I just find it easy to write and understand and plus I enjoy the syntax.
                                                                  When most people have a rough idea of something they want to make they do up a prototype in Python, but I usually do the same in Go just because I enjoy writing it.

                                                                  1. 1

                                                                    Compared to C for private and non enterprise projects Go offers:

                                                                    • No requirement for header files
                                                                    • Sometimes less verbosity
                                                                    • Nicer Errors
                                                                    • Faster build times

                                                                    While Go has good tooling C might still have more.

                                                                  1. 1

                                                                    At home:

                                                                    • Git Mirror
                                                                    • Go modules proxy
                                                                    • Gitlab-runner (Intel i5, 8GB RAM)
                                                                    • NAS (Synology)
                                                                    • Multiple low-traffic websites (RPi cluster + Cloudflare)
                                                                    1. 1

                                                                      I enjoy reading about such solutions. It would be nice to have a separate service that handles:

                                                                      • shutdown on device unplug
                                                                      • dead man’s switch
                                                                      • warrant canary update

                                                                      I can even suggest a name: rwulbrichtd.

                                                                      1. 1

                                                                        I find the approach with the battery status remarkable. I do not use the status bar at all and I used to check battery life explicitly with apm. Thanks for sharing.

                                                                        1. 3

                                                                          I am using the same approach. I have learned it from the video authored by Mat Ryer: “Idiomatic Go Tricks” (timestamp 17:01) from 2016.

                                                                          I wanted to generate mocks first, but then I realized that writing them manually is a great way to ensure that interfaces stay small.