[ $davids.sh ] — david shekunts blog

🤤 "Go for Dummies" 🤤

# [ $davids.sh ] · message #208

🤤 "Go for Dummies" 🤤

I was initially offended, but then I realized it's the best compliment to the language

#go

  • @ [ $davids.sh ] · # 1002

    Articles on this topic are most often written: "Oh, I have to write lines of code, and where's DRY, and where's KISS, and where's OOP, and why is everything procedural, and where's my simplicity" – but this is (I was recently taught with a phrase) "farting into a puddle."

    There are real reasons why Go is considered a language for "weak developers," and the 2 main points are:

    – Lack of memory management – Lack of concurrency management (you can do it with code, but at the level Go allows)

    This means a Go developer can write an application without thinking, which will be simple and fast for IO, but won't be able to bring it to "ideal."

    But that's fucking awesome!

    I love Go for its main feature – strictness and boundaries allow a junior / tired / developer coming from another language to write an application that will be 10x faster and more resource-efficient than if they wrote it in any other language, and the code will be understandable to absolutely everyone.

    Once, I joined a team of 5 people as a team lead, where everyone had only been coding for 1 year, and they managed to write an application that had tens of thousands of active users (news feed + chat), and they didn't have to think about speed or resource consumption at all; Go did everything for them.

    When I streamlined the development process, we started releasing once every 1-2 weeks instead of every 2 days. By the time of deployment, the services started "lagging." Server metrics showed that RAM and CPU were normal, but there were millions of open descriptors on it. It turned out that the guys simply didn't know that you need to close descriptors manually (!!!), and since they deployed every other day, the descriptors reset to zero each time, and the problem wasn't visible.

    Imagine: Go was capable of running for weeks with thousands of RPS, and it was like, "Well, millions of descriptors are open, so what?"

    And we didn't have any Kubernetes or anything like that: all microservices were simply deployed on several machines with 1-2 instances each using docker-compose and an ansible book for redeployment.

    That's it.

    The ability to super-simply and quickly write an understandable and fast application with just two brain cells is the best feature for any programming language.

    On top of all that, you can control allocations in Go at very many levels, even write and use your own malloc and free-list.

    But I'll describe that in another post.

  • @ Vova hardvair smartvend 🛍️💻 · # 1019

    and at the same time, they didn't have to think about speed or resource consumption at all, Go did everything for them

    Nothing personal, but when I'm told "a developer doesn't have to think about resources, XXX will do it for them" -- it should be understood as "no one can estimate how it will need to scale"

  • @ [ $davids.sh ] · # 1020

    When you have a task of processing 10-100,000 IO operations, where you take something as input, modify it according to business rules, and send it further to another IO (database, cache), you can, of course, write it in a language that has memory management, but you'll have to deal with memory not because "we need allocation speed," but because you can't write such code any other way.

    The same applies to CLI utilities and even native applications (they access storage through native layers).

    It's unlikely you'll store much in memory; memory for you will either be for storing constants and temporary variables that will be processed in the business logic and then disappear.

    It's worth trying to ensure everything resides on the stack (and Go already has built-in code analyzers that will tell you what will be on the stack and what will go to the heap), and for this, Go has a lot of different approaches and structures.

  • @ [ $davids.sh ] · # 1021

    So yes, you can write a backend, CLI, or native application, for example, in C, but it will take many times more time, you'll have to spend a lot of time debugging random memory errors, and you're unlikely to get anything more than if you had just done it in Go and if 1 instance couldn't handle it (and that's millions of active connections), you would launch another one (even manually).

    The opposite situation is when you need to optimize for memory: for example, you have a memory limitation (microcontrollers) or you are writing a system that is memory-dependent (databases, caches), then there are no questions, you need memory management.

  • @ Artur G · # 1022

    I adore Go for its main feature – its strictness and boundaries allow junior / tired / developers coming from other languages to write applications that will be 10x faster and more resource-efficient than if they had written them in any other language, and at the same time, the code will be understandable to absolutely everyone.

    And it seems no one uses this, in job postings for Go, it's almost always 3+ years. 😁 So maybe this argument is not correct?

  • @ [ $davids.sh ] · # 1023

    Is there a server-side language where you almost always get hired with one year of experience?

    On top of that, I'm speaking from real-world experience: people either started with Go, or were mid-level developers in a language far removed from speed or typing (Ruby, Python) and then switched to Go, and their code was at least of a decent level with sufficiently high speed and resource optimization.

  • @ Vlad Proskurin China · # 1024

    That's right. I've seen Python teams rewrite services in a few months when the business started doing well and hit performance bottlenecks.

    Although some, you know him well, say that when you hit Python's performance limits, it's not because of the language, but because of the database – it's slow. @davidshekunts, what do you think of this comment?

  • @ [ $davids.sh ] · # 1025

    First, I've always said: data is more important than code

    If you need to finely manipulate data within the language itself, choose the appropriate language

    If it's better to move data to an external storage, choose a **suitable **storage

    Once you've chosen the storage, design the schema in a way that's suitable for future work, describe it, and start writing queries

    When reality shows you what you missed and where you made mistakes, optimize, refactor, and change

    **And here, you always need to look at what exactly is performing poorly: is the system slow because it's waiting for connections to the DB, or are the queries themselves slow? What are your Memory, CPU, and connection metrics on the database and service hardware? What about your code iteration latencies (Event loop on Node.js and stop-the-world GC on Go)? What about memory leaks?

    And only then, collectively, should you make a decision

    It might turn out that by optimizing database operations or partially/fully changing the database, everything will improve, or you might be hitting the limits of the language itself

    I'll write a post now about the reasons for switching from Node.js to Go and describe an example where performance becomes the reason for the switch

  • @ Vassiliy ITK Kuzenkov · # 1026

    I've run into the "legacy Go" problem several times now – everyone who writes in it does so abominably, and I'm leaning towards the opinion that it's impossible to write any other way in this language, precisely because the language itself is like that. It's passable for microservices, but average projects and average teams don't need microservices.

    Optimizations are also not all the same.

  • @ [ $davids.sh ] · # 1027

    Oh, that's interesting!

    Then two questions:

    • What exactly does "disgusting" mean?

    • What language do you prefer instead of Go?

  • @ [ $davids.sh ] · # 1028

    P.S.

    For any person, any language can be unpleasant, I understand that perfectly.

    I'm interested in what exactly you don't like.

  • @ [ $davids.sh ] · # 1029

    And in Go, many things might not be liked)

  • @ Vassiliy ITK Kuzenkov · # 1030

    1. Low-level things interfere with high-level things (lack of expressive means for good, generalized abstractions).
    2. The "build it yourself" approach doesn't work well in Go. The absence of a dominant framework, conventions, not the easiest glue, and the lack of clear approaches, combined with somewhat problematic dependency management (this isn't just a matter of taste; there are places where it's done well, like Cargo or Lein, for example).
    3. Code engineering culture. Yes, people here usually have a good grasp of the fundamentals and prefer to just "get it done." This includes complex testing (environment setup, see point 1), a love for premature optimizations, and neglecting other processes like documentation (this is fixed in large companies, but Go is usually a good choice for them for churning out microservices and infrastructure tools).
    4. All of this leads to pain in code maintenance – features are released slowly (even with Rust it's better, despite it being much lower-level and from a different domain + you need to be "smarter" – higher entry barrier, yes).
    5. The main headache for developers ))) – Go routines everywhere, everything on Go blocks.
  • @ Vassiliy ITK Kuzenkov · # 1031

    I personally prefer Clojure :D, although I mostly write in Node.js. Clojure is great for fatigue-driven development!

    Where we hit the limits of Node.js, I rewrote the handlers in Rust.

  • @ [ $davids.sh ] · # 1032

    1. Can you give an example? I don't quite understand "low" / "high-level" and "generalized abstractions" in this context.

    2. "Dominant framework" is a matter of taste. I'm all for the "learn a framework, then build everything yourself" approach because most of the time, the more a framework gives, the much more it takes away.

    Lack of conventions? What conventions are you talking about? The thing is, Go has a lot of them.

    "somewhat problematic dependency management" – I actually dropped Go for 3 years, and upon returning, the import by repo link and go vendor completely satisfied me. What problems does it still have? I remember something was wrong, but I don't remember what exactly.

    1. Do you mean application code documentation or library code documentation? If the latter, I've never seen a codebase without it. If the former, I've always followed the tactic of "code should be self-documenting," and this technique is quite easy to follow in Go due to the maximum straightforwardness ("stupidity") of the code.

    Regarding premature optimizations – in this context, I see that it's not about specific individuals but about the Go community as a whole, so I'd like to clarify examples of premature optimizations that are characteristic of Go code. I'm interested in what can be prematurely optimized in a procedural language with built-in GC and concurrency via the Scheduler (which the developer doesn't have direct access to).

    1. A very interesting point. In my personal experience and from what I've encountered in media, Go actually allows for very fast development and release, and subsequent code maintenance (because there's a minimum of magic, and the two most complex topics are handled by the GC and Scheduler).

    2. Yes, undoubtedly, when you give something to a developer, they will abuse it with immense pleasure. But with the presence of async preemption, even if you overdo it with goroutines, the scheduler itself won't allow them to run in sections for more than 10-20 ms, so in the end, your code will remain maximally fast (I haven't seen this out-of-the-box in other languages).

  • @ [ $davids.sh ] · # 1033

    Wow, I've never touched Clojure at all! Cool! Can you write about a couple of cool Clojure features? I'm interested from a language design perspective.

    Rewriting in Rust is a good option if you like Rust.

  • @ Vassiliy ITK Kuzenkov · # 1034

    1. We're writing raw SQL, raw JSON for Elastic, etc. No ORMs.
    2. Conventions are precisely "how to build your own quickly and well." Again, I have examples in Clojure and Elixir (but Phoenix has already won there).
    3. This follows from point 1.
    4. Everything is generally okay here.

    I completely agree that everything works fast and the language encourages writing fast-running code. That's its nature, but it's all at the expense of maintainability.

  • @ Vassiliy ITK Kuzenkov · # 1035

    Clojure 😄

    Here's my personal top list:

    1. Interactive development (VERY POWERFUL REPL) - a completely different way of writing code
    2. Libraries written 10 years ago are still relevant and working (the language hasn't changed since they were written)
    3. Open-hidden multithreading (future, core.async - very similar to goroutines and channels, but using macros, no need to add anything to the language)
    4. Synchronization primitives and STM
    5. Extensible language (meta-linguistic abstractions) and the practice of writing DSLs
    6. It's very easy to write "glue" between libraries (everyone prefers working with immutable structures, and they are easiest to integrate)
    7. It's a Lisp (with all its charms and things that will be difficult for many, and impossible for some to get used to)

    And for me, Clojure is a bit of a filter language, helping me figure out who I'll enjoy being on a team with, and who we won't align with in terms of values. Go is similar :D, I'm unlikely to align in terms of work values and practices with most Go developers who are ready to rewrite everything in Go.

  • @ [ $davids.sh ] · # 1036

    1. This is where we diverge) I'm generally in favor of using raw SQL, BUT with pre- (like xo) or post-introspection (sqlc), and I'm against abstractions like Data Source / Repository.

    I've described why here.

    1. Understood. For beginners, courses (which are of quite high quality) will take on this role, and for more advanced developers, it's an opportunity to look at open-source projects (of which there are very many in Go).

    2. Premature optimization at the expense of raw SQL code? This is more about choosing the absence of abstraction, which precisely allows for writing more "specific" (i.e., optimal) code rather than "universal" code, without losing clarity and expressiveness.

    3. How exactly does coloring manifest itself?

  • @ [ $davids.sh ] · # 1037

    Regarding maintainability: for me, it means compliance with these points, and Go complies with them better than other languages due to its design.

  • @ [ $davids.sh ] · # 1038

    But again, personally, I see that we just have different approaches and different tastes, that's all)

    To each their own, and thank God there are a bunch of different languages for us to choose from.

  • @ [ $davids.sh ] · # 1039

    Corrected to "clojure")

  • @ Vassiliy ITK Kuzenkov · # 1040

    1. Projects came to me with 3 pens, with a full set, including in-place algorithms in fat methods and manual parsing) in general, the guys had fun as they could.
  • @ Vassiliy ITK Kuzenkov · # 1041

    I wrote nonsense about the coloring book, of course). In general, they just place them where they should and where they shouldn't. Nothing more)

  • @ [ $davids.sh ] · # 1042

    Well, you said it, and yes, I remember when I once joined a team and the entire service was written in one huge file with bitwise operators instead of == or ||.

    But the problem there was more that the person was purely a C programmer and was told "make a service with massive IO," and they had no choice but to pick up Go and start writing.

    Overall, I would phrase it like this – it's like with Armenians (I'm one of them): I can understand Moscow Armenians, and in principle, Yerevan Armenians too, but I fear Sochi Armenians like fire.

    People who come to Go from Python, Ruby, Java, C#, or from scratch will most often maintain some normal level of abstraction, but C programmers who need IO (due to the lack of C-like alternatives with fast IO processing) will come and make a mess.

  • @ Vassiliy ITK Kuzenkov · # 1043

    Well, in Clojure they just won't do anything :D