Hacker News new | past | comments | ask | show | jobs | submit login

Ok, I understand that people that use Go has a different idea about it, but...

> Code duplication is more acceptable in the Go community than elsewhere.

> Many times, you’ll need to write that functionality yourself, or copy/paste it from a StackOverflow answer.

Am I the only person that this is regression? How can code-duplication be defended by 'simplicity'?

> Fewer dependencies means fewer things that break while you let a project sit idle for a couple months.

I thought that pinning dependencies is \the\ solution for this? I don't use Go, so I'm not sure about the Go packaging story, but is there no package pinning solution? Yarn & npm handles this beautifully...




> Code duplication

It can be easier to maintain a small amount of code that you duplicate and check in and is purpose-built to your use case and treat as your own, compared to a general library imported for one routine that you have to think about (licensing, upgrading, auditing). Or sometimes libraries break backwards compatibility to add features you aren't interested in, and it makes busy work to stay up to date.

> Is there no package pinning solution?

Multiple dependency managers have supported pinning since at least 5 years ago e.g. glock, dep, glide. Go Modules supports pinning.

> Pinning is the solution for avoiding breakage due to dependencies

Related to the first point, in general you have to do work to upgrade your dependencies. If not because you care about bug / security fixes that happen, then because you use ANOTHER library that has the same dependency, and that other library uses a feature only present in a newer version. Any time you upgrade a library it takes work to verify nothing has broken, and potentially work to fix things that do break.


> It can be easier to maintain a small amount of code that you duplicate and check in and is purpose-built to your use case and treat as your own, compared to a general library imported for one routine that you have to think about (licensing, upgrading, auditing). Or sometimes libraries break backwards compatibility to add features you aren't interested in, and it makes busy work to stay up to date.

The overhead you're talking about isn't a real problem in my experience, as long as your dependency manager has semver support (all of those I know).

When writing (or copying from SO) a simple snippet that's supposed to do just what I want, almost 80% of the time there will be a bug in it (typically edge cases are not covered)…

For instance, before Go 1.10, there was no rounding function in Go's std lib, and 99% of the code you could find on the internet to do this (simple) task was buggy: some failed with negative numbers, and others due to bad handling of floating point numbers…


A lot of the answers on stack overflow are garbage. That whole site sometimes feels like the blind leading the blind.


> It can be easier to maintain a small amount of code that you duplicate and check in

> compared to a general library imported for one routine that you have to think about

IMHO, that's why small, one-utility-function libraries in the JS ecosystem([0], [1], [2]) are useful. (Unlike the general tone in HN where npm and left-pad gets a lot of hate.)

Look at Quickutil[3] for examples of other languages. (In this case, Common Lisp.)

[0] https://github.com/sindresorhus/mem/blob/master/index.js#L7

[1] https://github.com/sindresorhus/tempy/blob/master/index.js#L...

[2] https://github.com/zeit/ms/blob/master/index.js#L48

[3] http://quickutil.org


My current mayhem is containerizing an object detector pipeline/system with dozens of gigabytes of dependencies. The main dev has pulled in a library that makes opencv look like small potatoes, just to be able to use a whopping two functions. Apart from the misery that is a 4 hour container build every time something breaks, someone upstream broke my implementation (python 2, because ROS, ugh) with a casual '@' for matrix multiplication.

Yeah I love DRY and code reuse as much as the next guy, but sometimes copypasta makes sense.

I will say go's nature and/or culture make grafting in functions a lot easier.


One thing I omitted in the response which might be relevant is that usually you can only use one version of a package in any given build[1], so you have to use a version that works for everything that depends on it.

I think that might be a difference for folks coming from other ecosystems.

[1] The asterisk is that you can have multiple major versions coexist using Go modules, or you can use gopkg.in import paths, or some other workarounds, but in my experience that is not common and typically you do have to find a single version that works for your whole program.


I do not think Go Modules supports pinning except at the major version level and even there v0 and v1 are treated as fluid.

My experiences trying Go Modules has shown it to “randomly” modify the go.mod file.


The go.mod file implicitly pins to the lowest version that meets the requirements, as I recall.


Often developers create an abstracted mess to avoid code duplication.

The code is hard to follow and modify later if the common functionality needs to be handled differently.

There are many times when working on my old code that I was burned by this.

Code duplication isn't necessarily a good thing, but the cure can be worse than the disease. It is better to be pragmatic.


>Am I the only person that this is regression? How can code-duplication be defended by 'simplicity'?

Easily. People can avoid code-duplication in many ways, and some of them are harmful.

E.g. by building abstractions that get out of hand and add mental burden to track the program flow and bugs.

Or by making any small piece of code into a standalone 2-3 line function, so that anything always happens elsewhere, and keeping track of flow is made difficult.

Or by prematurely putting code into a function when it's just used in 2-3 places, and then when another need arises that doesn't fit in to the already captured function, hacking the function to handle different behaviour on different arguments/flags and ending up with monstrosities.


Code-duplication and goto statements have something in common: neither is inherently bad, and both are zealously maligned inappropriately by the developer community in aggregate. Sometimes two functions perform the same abstract operation on the data but have subtle differences in the implementation: fully duplicating the common parts in two different functions is not a bad thing necessarily. But "testability" and "reusability" zealots will turn blue in the face arguing that one should carve the functions up into a mess of abstracted spaghetti to support them both. The result is code that isn't any easier to read or maintain and, worse, is less performant.


Code duplication reduces coupling between multiple unrelated pieces of code.

Edit: not sure why the downvotes, perhaps these people have never seen a junior engineer go on a fanatical deduplication spree across a whole project, or perhaps these are the junior engineers.


A little code duplication prevents mounds of dependencies.

I’ve seen people link to multi-megabyte modules to save one line of duplicated code. At Google this was a disease. Good to see they’re fixing it.


Surely there is a middle ground.


"Don't make the code so DRY it chafes"


In Go, the informal rule is to duplicate the second time you need a bit of code, and factor out only on the third time.


Yes, the middle ground is to avoid duplication when code is non-trivial.

Most code is trivial.


I think there's more to it than finding the middle ground.

The question we should ask is whether or A should change whenever B changes. If the answer is yes then there should be a dependency. If the answer is no then no dependency should exist, even if some lines of code happen to be identical at a particular point in time. (That's probably what you alluded to when you said "unrelated pieces of code" and I agree with that).

The number of lines of code shouldn't be the primary concern though.

For instance, if some tax is calculated in a particular way as a matter of law, then that calculation should exist exactly once in the entire code base, even if it's just one line of code.


> The question we should ask is whether or A should change whenever B changes.

Spot on.

Avoid applying DRY principles to incidental duplication.


Exactly right.


It's not just junior engineers. Senior engineers do this, too: fanaticism and adherence to some set of practices no matter what, regardless of the context are as much a bane in this industry as chasing the latest shiny is.


It’s easier, to me, cause I can open almost any Go source file and expect pretty much some vanilla looking code and not some cleverness from a dev that is no longer working there for whatever reason.

Look at our fiction and non-fiction writing; very few real plot loops and themes. There’s a common wisdom in writing I’ve heard called “kill your darlings”, those bits of prose that you love in isolation, but big picture, really don’t add to the detail of the world or motivation of the character

I do the same with code: keep code itself vanilla af to avoid complicating or distracting from the big picture of its value

Oh and that’s at work. To be certain, I fiddle with favored abstractions and ideas on personal projects when the mood strikes. I cram assumptions into abstractions because it’s just me.

But at my job, where I only go because of social pressure to conformity, I don’t really want to wade through other people’s emotional opinions. I just want to copy/paste together same old to keep it easy.

Give a programmer a job building some thing real like a car and you’ll get The Homer.


As with anything else this depends on context (code, assignment, goals, etc). One sentiment that I share with OP is that somehow, you do feel less pressure to over-dry than other languages. Probably because (1) more code doesn't necessarily feel like more bug surface (2) you can avoid a class of issues like parameter (mis)usage and excessive branching that you would need to satisfy de-duping.

Obviously trivial cases of code duplication could/should be resolved, but for less than trivial cases sometimes its just fine to cmd-c & cmd-v with some minor use-driven modifications.

Remember the lib versions of many of these snippets need many many parameters to satisfy everyone's needs.


Most people are thinking of gos simplicity vs. javas over abstraction of oop. I personally prefer the ugliness of go to the over abstraction of java.

However go is missing fundamental concepts of algebraic data types that in itself is causing problems. In their effort to create a language that moved away from the flaws of OOP they failed to have knowledge about type theory and created a language that in removing the flaws of OOP they have also accidentally removed fundamental language concepts. My guess is that they didn't know about the theoretical concept of sum types at all because sum types exist in Java as inheritance.

The two biggest being parametric polymorphism and sum types and it is these two things and the consequences of these two things that are causing the ugliness in go. Everything from default zeros, functions that return both errors and values and repeated code.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: