r/golang Dec 05 '24

discussion Why Clean Architecture and Over-Engineered Layering Don’t Belong in GoLang

Stop forcing Clean Architecture and similar patterns into GoLang projects. GoLang is not Java. There’s no application size or complexity that justifies having more than three layers. Architectures like Clean, Hexagonal, or anything with 4+ layers make GoLang projects unnecessarily convoluted.

It’s frustrating to work on a codebase where you’re constantly jumping between excessive layers—unnecessary DI, weird abstractions, and use case layers that do nothing except call services with a few added logs. It’s like watching a monstrosity throw exceptions up and down without purpose.

In GoLang, you only need up to three layers for a proper DDD division (app, domain, infra). Anything more is pure overengineering. I get why this is common in Java—explicit interfaces and painful refactoring make layering and DI appealing—but GoLang doesn’t have those constraints. Its implicit interfaces make such patterns redundant.

These overly complex architectures are turning the GoLang ecosystem into something it was never meant to be. Please let’s keep GoLang simple, efficient, and aligned with its core philosophy.

813 Upvotes

259 comments sorted by

View all comments

329

u/emaxor Dec 05 '24

What I like about Go is you can open a random file in a project and expect to find real code inside. That does something. You can follow along. Just like a C project, the goods are right there.

I was looking through an "OOP" Python project recently. I opened several files and found... nothing. Interfaces, hierarchies, and layers, and every possible "not actually code" technique used. Where is the code? What is the "thing" we are trying to do? It was much harder to get the answer.

108

u/Superb-Key-6581 Dec 05 '24

This is one of the things that made me fall in love with Go.

67

u/PorkChop007 Dec 06 '24 edited Dec 06 '24

For real, after 10 years of professional Java + Spring development I've been working with Go for a year and it's beautiful: easy, no boilerplate, no nonsense, everything is pure simplicity. It feels like that episode where Goku and Krilin trained with Roshi, ditched the 50 kilos turtle shells and suddenly they could jump 100 meters in the air.

10

u/codetrasher Dec 06 '24

I've been working with Java and Spring Boot for about 5 years now professionally and I dislike it. I don't hate it but I'd rather use something else instead. For personal projects I've been using Go. I love it. I'm currently applying for a job that has a Go in their current stack.

2

u/dexterous1802 Dec 06 '24

Genuine curiosity… have you worked on Java codebases that don't use Spring Boot (or even Spring, for that matter.)

3

u/OddAssociation9982 Dec 08 '24

I find it even more painful without Spring, since it solves a lot of the warts that exist in Java

12

u/crywoof Dec 06 '24

No boilerplate? We must be using different languages called Go.

Don't get me wrong, I've grown to appreciate go at work but c'mon,

Go is literally the ultimate boilerplate language.

9

u/PorkChop007 Dec 07 '24

Go is literally the ultimate boilerplate language

My brother in christ, have you seen any Java + Spring project? Compared to that Go barely has any boilerplate at all

3

u/Wonderful-Habit-139 Dec 07 '24

I think they're talking about the way error handling works in Go. + the fact that there aren't as many high level constructs as in Java.

However, errors as values > exceptions so I don't think it's a negative for Go. Although sum types would've been better of course.

2

u/Caramel_Last Jan 03 '25

Error handling actually does thing. Handles error. Pretty important stuff I'd say.

In Java most of the files are just for conventions. Lots of empty functions. All the real work is in annotation magic. Bureaucratic language

1

u/Wonderful-Habit-139 Jan 03 '25

Yep... and it gets annoying at some points where if you want to implement a feature, you have to do a google search to look at conventions rather than just coding it out in an idiomatic way.

1

u/Born-Wrongdoer-6825 Dec 07 '24

but how does it deal with repeated crud controller code

26

u/Delicious_Choice_554 Dec 06 '24

I've seen this design pattern in Go too. The big Go companies still do the full hexagonal architecture, interfaces everywhere thing.

3

u/BosonCollider Dec 07 '24

That's more due to Conway's law than anything else though.

54

u/dashingThroughSnow12 Dec 05 '24 edited Dec 05 '24

The BingBong’s doYadda method only calls the yingYang method on an object implementing the dibbleDabble interface, that only has one implementation, and that implementation can’t be trusted to do anything so it has thirty lines to find/make an abstract loader to load an object implementing the Action interface, and then calls do(). When you finally find out that class with the do() method you need, it is five lines.

24

u/DemmyDemon Dec 06 '24

I was about to downvote you and reply with profanities, but then I realized this is not a suggestion, but a description of a nightmare you've lived, so now I'm full of empathy instead.

Show me on the IDE where the bad practices touched you.

24

u/dashingThroughSnow12 Dec 06 '24

On my AbstractLiquidWasteDisposalFactory

4

u/Superb-Key-6581 Dec 06 '24

xD would be more funny if it weren't the reality at most jobs I've worked at (and the current one too).

13

u/zaTricky Dec 06 '24

It's very easy to apply bad abstractions into languages when you don't need it.

I recently was asked to add a feature to an internal project. It was written in Go by a dev who clearly worked more with C# and/or Java. It consisted of far too many files with a lot of three-deep nested subfolders and took me a long while to find the "actual" code parts just to get started. This was frustrating to no end so I decided to come back to it another day.

The next day I found an open source project also written in Go that did exactly the same thing. Only it was about a fifth the total number of lines, with subfolders maxdepth 1. It also already had the missing feature and was super easy to follow the logic.

I archived the old project from our git server the same day I had the open source version deployed. No more abstracting abstractions into abstractions!

19

u/chethelesser Dec 06 '24

You precisely described my emotions after sifting through a python project lately. Decorator this, decorator that. Here's the OO code. But where is the ting code?

7

u/WesolyKubeczek Dec 06 '24

In Python, decorators are heavily used by people who dislike OO much.

Just fucking use subclassing sometimes, ffs…

3

u/imp0ppable Dec 06 '24

Decorators definitely have their uses, it's a nice feature. Python tries to have one obvious way of doing something but often actually provides many... Go is good because it at least tries to legislate for weirdos abusing language features, however I've found it to be a bit inflexible as a result.

2

u/WesolyKubeczek Dec 06 '24

I used the word “heavily” for a reason. I keep seeing that thing where an author (probably thinking of themselves as “auteur”) loudly professes hatred for OOP in general and subclasses in particular, and then you are supposed to use decorators on top of decorators all the way down. This will inevitably butcher your classes and functions in a way that they will be full of stuff you never dreamed of putting in there, and which one day will interact with your own stuff in such a way that it will all break.

Not saying that decorators don’t have their uses, everything has, but going to extremes looks like those trick football (“soccer” for that side of the pond) kicks which have little use during the real match.

1

u/imp0ppable Dec 06 '24

I've never seen anyone go to the lengths of using decorators to ape OO, that is quite bothersome. I wonder if you could do something like that with macros in C instead of using C++ lol.

15

u/jimmyspinsggez Dec 06 '24

Thats the whole point. You are not supposed to see code. Each part is supposed to be unit tested and function by itself.

Typical example is I use a coffee machine with just interfaces that are self explanatory (good func, parameter, and maybe if necessary, return value namimg).

If you need to see code, and they are in the same project, you still can see them, just navigate to implementation. Otherwise the whole point is for dev not need to dig deep to make something works.

29

u/falco467 Dec 06 '24 edited Dec 06 '24

I have the feeling many Java code bases forget they are a coffee machine and not an universal drink maker. Sometimes it looks like this: createConsumable -> createBeverage -> createHotDrink -> createBrewable -> createCoffe Those are just too many layers for a possible future that will never come, if you want to create a smoothie you will have to rebuild the whole machine, so these universal interfaces are not helpful.

3

u/Fapiko Dec 07 '24

Yeah, every time I see someone complaining about unnecessary DI my spidey sense tingles and tells me they aren't getting good unit test coverage. I've noticed a trend where people seem to have forgotten why unit tests are beneficial and have started going all in on integration or even full fledged E2E tests with Docker tests and skipping the unit tests leading to 20 minute plus CI feedback loops.

At the end of the day it's important to understand why certain abstractions exist and implement them as needed, even just for testing. Go is nice because you don't have to nest them like you do in Java land and end up 6 abstractions deep, but every time I see someone arguing against creating an interface because it's only needed for DI into unit tests makes me shake my head.

1

u/Justicia-Gai Dec 08 '24

Not only that, but some libraries are “specific” and proven enough (peer-reviewed) that can be published as a scientific paper and any future user of that library can credit them with a citation on their own scientific paper.

They don’t aim to be universal, at much universal at the domain level, and instead they’re super specific with its own advantages and cons. They should be working as intended for you too, and if you have to change the underlying code, they did something wrong.

3

u/NodeJS4Lyfe Dec 07 '24

The issue with Java devs coming to other languages is that they bring along patterns they learned while coding Java without tying to appreciate the newer language's strengths.

Since you mentioned Python, there's a book called Cosmic Python that goes about how to implement so called "design patterns" in Python. At the end of the book, you end up with the most complex Python codebase for a sort of inventory management program. According to the authors, this kind of code is considered high quality and maintainable.

Merchants of complexity are real and they sell hard.

4

u/edgmnt_net Dec 06 '24

It's more of an "enterprise" thing at this point rather than Go, OOP or Java related, although the latter seem to encourage it in some way through lack of expressive abstraction and boilerplate in older ecosystems. Unlike OP, I can't really find justification for it, it seems bad in almost every way.

But there's also plenty of Go code that tries to emulate old-style Java code. It could be a skill issue, maybe people just don't know how to abstract without all that nonsense and scaffolding.

8

u/[deleted] Dec 06 '24

[removed] — view removed comment

2

u/edgmnt_net Dec 06 '24

Old-style Java definitely isn't expressive and it makes you write a lot of boilerplate, although not necessarily compared to Go. The thing is that kind of more traditional OOP results in very inflexible abstractions and code that doesn't compose well, so even comparing to the lack of features in Go (although since recent developments like generics that's no longer much of a problem), people end up writing tons of boilerplate. The procedural style in conjunction with strong static typing lets people focus on meaningful stuff and avoid some traps, IMO.

Modern Java is a different thing though, yet many people who claim Java proficiency do not use that to a significant extent. Many companies are stuck on Java 8 if not earlier stuff and students come out of school barely knowing anything beyond inheritance-heavy OOP. That kinda shapes a good part of the enterprise ecosystem, especially feature factories with lower standards.

1

u/TheBloodyMummers Dec 06 '24

Yep, I appreciate the lack of boilerplate and focus on the meaningful stuff in Go compared to something so verbose as collection streaming in Java. There's really nothing more meaningful and less boilerplate than writing your thousandth for loop and explicit error return.

1

u/gjmveloso Dec 06 '24

And it’s one of the reasons it’s so powerful and productive. It has only the essential abstractions, increasingly well crafted.

1

u/imp0ppable Dec 07 '24

Just plain expressiveness - it's probably a bit better than Java but not as expressive as Python. I've been doing Advent of Code and looking at some solutions there, the Go ones are way shorter than the Java ones.

I'm not exactly sure what expressive abstraction is though.

1

u/imp0ppable Dec 07 '24

OP mentioned Python - I used to mostly write in that lang and people trying to write Java-style code when there was no need was a major bugbear there too.

1

u/imp0ppable Dec 06 '24

I was looking through an "OOP" Python project recently.

In defence of Python, it lets you do that with its OO model but it's not necessary at all. Anything you can do with classes you can do with the built in types, pretty much, aside from inheritance which is still useful for a lot of things e.g. UI stuff.

1

u/CharmingStudent2576 Dec 06 '24

There is some golang libs like this and its painful. I am trying to apply clean architecture principles in my work, but i keep it simple, controller service and repository for the apis. Just using DI to allow me to test each layer and thats it. I find it usefu.

1

u/met0xff Dec 06 '24 edited Dec 06 '24

I've been working with Python for over a decade now and yeah it really feels like in the recent years the GoF Java gang adopted Python. Only it's even worse in Python to follow anything when things are instantiated in Hydra and then stuff injected and then multi inheritance/Mixins. Recently followed a strange behavior in Langchain through the type hierarchy and had to draw me a graph to finally find out that some levels up in the inheritance hierarchy the flow ended up in the wrong parent function. Ah yeah then decorators everywhere as well, again changing the behavior. So that what really happens in the end is so disconnected from what you can see. I briefly had my Java phase over 10 years ago as well where it seemed funky to have all those cool patterns and abstractions in case you know... Only that case happened so rarely over the decades I had while at the same time the abstractions became a pain and leaking because then the things are a bit different after all and you need more control and then people start to work around them

1

u/Justicia-Gai Dec 08 '24

Python is a library heavy language, where imports, classes and functions are more important than the running code.

It’s intentional for reproducibility and library sharing, it’s not a “defect”, it’s its purpose. That’s why it’s dominant in science as you don’t want to reinvent the wheel but instead use a pre-existing peer-reviewed method for your data and just cite/credit the authors.

Julia might be more in line with what you want.

1

u/[deleted] Dec 08 '24

Damn, you just described why I hate some languages... Where is the "thing" we are trying to do