I couldn't disagree with this any harder. I'm a Java BE Engineer who joined a Go shop, so I have a direct comparison. Both are microservice environments for products of similar complexity.
It is insane to me how much less productive Go is for your average microservice enterprise environment. I'm sure it's great for systems programming or tool development. I like the simplicity. But the ecosystem is borderline useless for larger scale enterprise-y service landscapes. GRPC and Protobuf are overengineered and underdocumented, a lot of things are seemingly optimized for Google's specific needs.
We are more people, we are more experienced and have a decent engineering culture. Yet we're definitely less productive than the last average, traditional Java Spring Enterprise team I've been in. "Circumventing" a framework restriction (which rarely happens if you stick to good practices) is MUCH less effort than building things yourself from scratch.
You're saying that grpc and protobuf are over engineered but you're happy with Spring?
gRPC and protobuf are just transport and serialization, they have nothing to do with business logic, on the other hand Spring is a heavy, bloated framework.
Most Java frameworks are complicated backed by layers of abstraction and black magic.
btw no framework does not mean you don't use any library, there are some good lib aka micro framework that have everything you need to build modern and decent api servers.
> btw no framework does not mean you don't use any library
I hear that silly argument "no framework === rewrite everything from scratch" far too often.
There's a giant difference between libraries and frameworks! It's terminology: it's a framework if you build your app inside it. It's a library if you build it inside your app.
The later is fine. The former: I dislike it - even in JavaScript, Ruby, Rust or anything, I wrote a longer post on that[1], which got a lot of discussion on HN. Most of it too in the line of "lol, I use a framework because I don't want to write it all myself", completely missing the crucial first paragraph in which I carefully tried to explain the difference and explain that re-using code != using a framework.
If you don't use a framework, the structure of your code will still grow to resemble one anyway. Something internal, nonstandard, more difficult to maintain, and probably less congruent with the problem space.
> You may feel that building your services without a framework will take ages. Especially if you are coming from other programming languages. I understand that. I had the same feeling a couple of years ago when I started writing in Go. It was an unjustified fear. Not using a framework doesn’t mean that you will need to build everything yourself. There are many proven libraries that provide the functionality you need.
> more difficult to maintain, probably less congruent with the problem space.
This is not true as blanket statement. It may. But with "a framework" you are bound by the architecture, upgrades, use-cases and so on that this framework covers. And limited by the ones it doesn't.
In practice, choosing a framework on day one of the project, means you cement yourself in architectural choices when you still lack all information about what architectures will be needed. You don't know your problem space.
All you know, for certain, is that his problem space will turn out different than what you thought it would be today. Flexibility to move along as this evolges is critical to "maintainability".
In practice, therefore, you'll quite likely end up with a framework that is severely harming your ability to write maintainable and congruent code over time.
But for those who do use a framework the problem space and failure modes are quite similar: some succeed at fitting in the unavoidable domain code into the blanks left by the framework, others build a "framework within the framework". And occasionally that might even be the right call, because the framework+blanks fits some of the requirements so well, while others exist that are served well by the "framework within the framework". Does not contradict "most framework within the framework are horrible mistakes" at all.
That’s because your premise is weak. You argue that Django is the wrong choice for lots of projects but the counter factual is that our job is to figure that out. We have to choose the lesser evil.
It’s not convincing to just call everyone stupid. Everything is a trade off. You have no idea what those could be in any particular situation so that’s why your whole argument falls apart
Then you focus on construction and coding and ignore everything else a framework offers, which has downstream effects on coding and construction. It’s not all about the code. The coding is actually the easy part
The only projects ppl pay us is for the ones that are so large and complicated that if you don’t use frameworks, you’re going to either die or go crazy
This reminds me of an debate I was having with another dev who said python was "pointless" and that there was a better, more elegant way to do things.
I asked him what is a better way to quickly/easily stand up microservices/apps for clients (90% of my job), if not Django (or Flask/FastAPI, etc).
His response was "django is great, but it could have been written better and in another language" ... well, sure, great... but that doesn't solve my problem, lol.
We use Django for tons of client projects and rarely ever hit the "limitations" wall. It is mostly CRUD apps, so that helps but I would argue the time saved using frameworks in our shop is much more than the time lost spent on working around limitations. I do think the author has good ideas tho, good principals... but if you know your craft well, you know what will/won't work in a framework and how to solve that.
Yep! I just thought it was mostly funny how a "better solution" is claimed and then after looking at the problem that solution only exists in theory, lol.
> It’s not convincing to just call everyone stupid. Everything is a trade off.
I wish it were less acceptable to play the "balanced" person in the middle.
If and when there is a balance point, someplace where the actual truth tends to be, imho, it's almost never in the middle.
There are reasons those frameworks (and Golang, too) tend to do things in an opinionated way.
I wish there were more strong opinions lightly/loosely/weakly/gently/another-word-ly held[1]. I think opinions too strongly held is a surer path to there than starting from a place of trying to "meet in the middle."
[1] DDG-ing the term showed a bunch of adverbs of holding! Here are a some:
why it works: https://www.nwea.org/blog/2022/strong-opinions-loosely-held-demystifying-social-emotional-learning/
someone also said something on medium: https://medium.com/@ameet/strong-opinions-weakly-held-a-framework-for-thinking-6530d417e364
contrarians take a stand, too: https://commoncog.com/strong-opinions-weakly-held-is-bad/
> there will be a lot of projects where Django is a very poor choice.
often and lot is crucial - it's opposed to all. Because it implies exactly what you then continue to state: that its our job to figure out if this project is one of those "lot [..] with a poor fit" or one of the ones where it actually, and will remain, a good fit.
Yeah, look, you waffled this one but I still liked it. I can tell that you know what you’re doing and that you have something to say. I may not agree with it but I didn’t dismiss it.
But when I say premise is weak, it’s a technical term. Doesn’t mean it’s wrong.
I mean, in this case I do think that, but that’s why you’re getting so much pushback. Even if the premise was right, you’d still be getting pushback.
On the next post, remember to spend extra time there. It always comes back to haunt you.
Remember that the reader is smart but in a rush. The less words the better. Keep it country simple.
> You're saying that grpc and protobuf are over engineered but you're happy with Spring?
This was my reaction. Every Spring app I've been involved with was a nightmare of gratuitous complexity that was nearly impossible to debug. I'm sure if I were a master of Spring I could figure it out, but that's the nice thing about Go--pretty much any programmer could work out what's happening even if they aren't particularly familiar with Go. You don't have to trace something from XML to Java, and there is basically no magic (maybe the odd bit of Go reflection is the exception to the rule, but it's much less common than in Java/Spring and the "magic" is much less magical).
You can use yaml nowadays with Spring. It's not anymore that ugly stuff it used to be 10 years ago where you had to wire everything together bit by bit.
I believe one thing that really tells the Java world apart from others is the heavy reliance on DI containers like spring/karaf/osgi etc. Once you understand that, everything is simpler.
I would say "believe me" in a face to face conversation, but even then it's proven useless :)
Working with Spring for me has become one of those things like when people suggest to "choose a boring technology" to build something. Yes, that's it. In the positive sense, of course.
There is literally an easy integration with everything you need, the learning curve is relatively smooth, and yes while it's true there are quite a few annotations you need to get used to, I believe after a few days you finally get used to it, and finally it simplifies a lot your development experience rather than making it worse.
For me the only reasons I would pick Go is because of native binaries (smaller footprint, memory, cpu etc), and it's "slim" for simple programs (like Python, but again binaries/native). I also like a lot Go's syntax so that's another pro.
Finally they are both very solid languages with strong tooling and wide communities.
Ps: the cool "new" guy seems to be Quarkus, though :)
Spring Boot is actually a VERY easy to use framework. So much so that Netflix shifted out of writing their own libraries to using Spring boot.
For example, the code below is a complete Spring Boot application with all of the default configuration in place. It will take just a couple of minutes to have this running and it provides quite a lot of features under the hood - which you don't need to worry about.
@SpringBootApplication
@RestController
public class DemoApplication {
@GetMapping("/helloworld")
public String hello() {
return "Hello World!";
}
}
I write Spring full time these days. You may have Stockholm at this point (like me), but Spring is terrible in two areas:
- when things go wrong
- onboarding newer / more junior devs
For point 1, there are so many layers of abstraction and 20 page stack traces that you could fill an entire log buffer with just one NPE...
Kidding aside, I can't tell you how many times I've wrestled with the auto-configure magic. The reality is you'll include so many "starters" in a medium use app, you won't know whose including what. A polluted Spring container is a real problem. That isn't the only problem, but it's one of the more prominent. You may say "well write cleaner code!" and I would reply that Spring is conducive to writing code that doesn't fit well with the framework, and that's mostly because you have to understand 10+ years of architecture decisions when you want to do anything beyond the basics (That's why we mostly don't reach for the "Spring" way to do things anymore, just the simplest way). All that is to say, there is a reason Spring development has been supported by Spring consultants.
For point two, It's very easy get started but it's very difficult to mature into a fully productive dev. The things juniors and mids struggle with the most is unpacking autowiring and how to resolve those issues, how to properly handle async behavior (especially with Spring fully embracing WebClient and Reactor now), and database connections.
But yeah, outside of all of that, very easy to use, sure.
> Kidding aside, I can't tell you how many times I've wrestled with the auto-configure magic.
Because everything in Go is explicit you can actually follow a short series of function calls when debugging instead of staring at a 50-function stack trace with a bunch of obscure Aspect4J magic while you feverishly search the Spring JavaDocs for whatever specific error message you're getting.
Go is a dumb language and I like it. Everything's obvious and you can't get too cute. Now that there are generics the only thing that I'm really jonesing for is a decent collections framework in the standard library... generic map, filter, fold, etc. on slices would be a real boon to productivity IMHO.
I'm a competent dev in a dozen languages including Java, I am not a professional Java developer. I've had several instances where I've had to work on a project using Spring and every time it was a freaking nightmare. Not because I am not capable, not because it didn't work, because Spring took everything and shoved it behind 15 layers of abstraction and told me to figure out the magic incantations to get it to do what I wanted. When I tried to figure out how Spring actually worked it was like trying to figure out a whole new language.
My contention is Spring is great for those who have been initiated into it's ways and like to be the members of the Martian priesthood that sing canticles to the Omnissiah, but for those who want to understand what is going on and why Spring is anathema.
is there a go-to third-party documentation for spring (eg a book) you would recommend? I find official docs difficult to navigate. (which are either very narrow tutorial or in reference format, with important details omitted).
It's usually search "baeldung spring <whatever I am trying to figure out>" and I get a nice article telling me how to do it but also explaining it as well so I can have the knowledge in the future.
I think the main author Eugen (there are many now) has a nice book.
> For example, the code below is a complete Spring Boot application with all of the default configuration in place.
Uh no, you forget the part where you have to add a bunch of stuff to your build system (maven or gradle, usually), so it actually knows what to do with this.
You can't just compile that class with java -c, run it, and have a running application the way you could do it with lighter-weight frameworks.
Yes, you have to learn how to build a minimal pom.xml to make this compile and run. IntelliJ will do this for you with its Spring Boot project template. After that, it really is this easy.
I consider the boilerplate needed to build a main.go file that starts a gRPC listener to be harder.
Writing a single get endpoint that returns string is easy in almost any language/framework today, you didn't really show anything with it. The real pain starts when you have to add database connection, migrations, auth, security and all that.
Better than an alternative is Connect, which is gRPC but also supports gRPC-Web and the lighter weight Connect protocol (which seems inspired by Twirp) https://buf.build/blog/connect-a-better-grpc I like it because it solves most of my complaints about using gRPC from Go without giving up on gRPC.
I use chi for one of my projects and though it was fun implementing everything myself. Having a framework would greatly reduce the length of my handler functions and the chance that I mess something up.
I had to:
manually manage sessions and jwt token
Implement message flashing
Even reading data from a form was a little too verbose for my liking
I’ll vouch for chi here as well: if I’m building a Go web service and I really need to go one step above http.ServeMux, it’s the only library I consistently reach for.
On one level, it is not an outdated take. There is a LOT of old Java out there: out of date JDKs, out of date versions of frameworks. There is still a lot of Struts 1.1 out there.
I think our industry would look a lot different if more companies were keeping their Java apps up to date.
I have to switch between Java Spring boot, Go and Rust. The latter two are framework less and easy to understand just by reading the code. Spring-boot development requires so much googling to figure out why I get UnsatisfiedDependencyException and what each annotation means. Even if I get it to work I still don't understand how it works.
I'm not sure that's contradicting the parent post.
A framework is an added friction to picking up a development environment but potentially a huge asset to velocity once people are up to speed. You haven't gotten properly up to speed in that framework and that site's use of the framework. Perhaps you never spend enough time in that domain to really need to pick it up, in which case you'll always pay the "WTF is this?" tax.
But it's equally likely that once a person's up to speed in the framework they're efficiency is greatly enhanced.
A programming language isn't just the syntax of the language; there's also knowing the common idioms of the language, understanding the runtime / build environment, knowing when and how to leverage the standard and extended libraries/package ecosystem, etc. Frameworks fall into the "extended package ecosystem" which is vastly different from language to language, and sometimes _within_ a language if the language is sufficiently mature.
Some of it is "just math" and some of it is understanding the culture.
Some of the pains with Spring Boot don't go away even when you understand it better. The fact that the framework relies so heavily on reflection makes debugging a pain, turns what should be compile-time errors into runtime errors (which, among other things, makes updating libraries annoying), and also leads to very slow integration tests. I also haven't yet met a developer who fully understands what @Transactional does, exactly.
I agree Spring Boot brings a lot to the table (Spring Security alone, for example), but at the cost of significant downsides.
I see the benefit for larger and more diverse teams, but personally I would choose lighter-weight frameworks and libraries, even if at the cost of having to do more plumbing myself.
> I also haven't yet met a developer who fully understands what @Transactional does, exactly.
I have found Spring Boot to be a step forwards from the old Java EE world in basically every way except for this. Transactional boundaries are somehow more difficult to analyze.
> You haven't gotten properly up to speed in that framework and that site's use of the framework.
I've been working in Spring framework for years, and don't understand it. I suspect most of the people who develop it don't understand it either, as it's so enormous it's probably impossible for any one human being to really understand it.
I'm not trying to defend this or that development environment or framework, just pointing out that there are trade-offs everywhere.
I don't understand speculative execution or any of the other deep voodoo that happens in modern CPUs that spend enormous amounts of energy presenting the "I'm a PDP-11/70 with way more memory and bigger registers" lie to programmers.
These days I'm not even sure which part of my computer is running the networking stack, or where my computer is located.
We stand on towers of abstractions, and complain about the ones we're forced to notice. I don't think many people understand "it" these days.
I mean, you sound like you try to use a tool without knowing it.. why exactly do we believe that it is okay? Would you expect to be able to use a crane at first try?
Spring boot does put a layer of abstraction over ordinary “linear” code, but it is not black magic, if you understand its DI part, you will basically understand everything (in short, spring is only allowed to generate new code, not modify existing one. So it works its whole “magic” by generating subclasses at runtime. With this requirement in place, most of its working makes clear sense. It has an internal list of available beans, and you can inject them by type only (@Autowired/@Inject) when it is the only bean of said type, or you may have to be more specific by naming the desired bean. And it is a simple graph of such injects.)
And it is a well-understood convention, above all. I can go to another spring shop and expect to see JPA entities annotated the way I’m used to (which is another very powerful feature. Can it be misused? Sure. Does it save a shitton of time, and less code means less bug? Yep. Plus, before the “ORMs considered harmful” people join us, they were always meant for OLTP, not OLAP, and as a mapping to/from db records, where they absolutely shine. Don’t try to OUTER JOIN on a subquery of whatever with them, but feel free to write your SQL and map its results to a business object).
I switch between Go, Spring, and ASP.NET (the latest one, which used to be Core, but isn't anymore). I don't think your Spring experience should be extrapolated to other frameworks — dealing with it is certainly significantly more painful than working with dotnet.
You don't get type safety for your database queries without jumping through hoops (compare anything it provides to LINQ — it's not even in the same league). Hibernate feels like a significant step-down after Entity Framework (I might be an idiot who doesn't know how to use it, that's entirely possible, but this just proves the point — it was easy to get started with EF, it didn't require a doctoral in ORM studies to become proficient with, it's dead easy to write queries for (thanks to LINQ), and the vast majority of those translate to pretty efficient SQL which is fine for 99% of my queries).
You get lots of behavior decided at runtime (while ASP.NET for the most part is very explicit in its configuration).
It also doesn't use many annotations (most of those I use are fully optional, like adding pretty names to database columns; and those can be configured through explicit method calls).
It does require DI (and IoC containers are swappable if you don't like the default), but I didn't have nearly as many issues with it as when working with Spring projects, whatever the reason may be.
From what I remember from spring boot it is (or feels like) more of a system to assemble your application from discrete modules, but which modules are used for what can be determined by configuration and various rules.
I mean the use cases of that will be limited these days; application architectures have changed to the point where what used to be a module within a Spring application can now be a microservice to be enabled or spun up depending on rules defined outside of the application.
But this is a very superficial point of view, I haven't seriously touched Spring for a decade and, thanks to some experience with Go, I feel like I won't need to either for most modern-day applications.
While Go may not be for everyone or for every application, its mindset of keeping things simple, just use the SDK or some libraries, etc does make for better developers. IMO.
This 100%. I think the people largely arguing for no frameworks have no idea what real productivity looks like at large scale engineering orgs. This mostly means you are not handcrafting libraries. There is a dedicated team who manages platform tooling including the frameworks/SDKs you use. Product teams may contribute to that but they will mostly be consumers. I always equate this to car manufacturing. I am not buying a kit car, I am not buying a hobby car, I'm buying a well engineered product from a large scale manufacturer. Frameworks and platforms fall into this category, especially for enterprises.
I think it's fine for small teams not to use frameworks, and maybe 200 person engineering orgs are made up of many small teams who just want to agree on a protocol rather than shared framework/platform but once the scale starts to increase you really need to start tightening up and implementing some better standards.
People constantly talk about not being Google, well let me tell you the people scale is all the same, the amount of legacy infra is all the same. If you haven't peaked into the depths of the messy multi-decade enterprise you have no clue. Your 4 year old company that you joined 6 months ago is no comparison to something with a legacy of 3 decades with 2k+ engineeers scattered across a disparate org trying to modernise in the cloud or whatever comes next.
“Real productivity at large scale engineering orgs” means a bazillion different things, because a big org can afford to have different teams specialise in different things.
No-framework Go makes the most sense to me, because a big framework doesn’t make sense for all types of project, and the set of projects where I’d reach for a big framework just doesn’t intersect _at all_ with the set of projects where I’d reach for Go.
It goes back to, what is a framework and what qualifies as a big framework here. I think classic rails isn't the fit, but something that's an extension of gRPC definitely works. What gets handcrafted is a lot of layers around gRPC or far more stuff around HTTP.
When I'm working on personal projects, frameworks don't make sense for me. When I'm trying to engineer something at scale e.g https://m3o.com then I need that standardisation at the platform layer, the framework layer, the API layer.
When you're building something "at scale", there'll be business logic API services, cache services, routing/dispatch services, and many other such things.
What I'm saying is that I find business logic-centric API services to be a good fit for larger frameworks that take ownership of more of the low level logic, and I don't find Go to be a good fit for those, at all. Inversely, I find Go to be a much better fit for the more "technical" services (provided we're not at the must-squeeze-every-ounce-of-performance C++/Rust level of requirements), but I also don't want a framework getting in the way.
Pretty much, not using some framework just means you will be re-writing same thing over and over again.
For our small-time ops use we ended up on Gin/Gorm/Zap/Opentelemetry + some code for internal SSO and it works fine but I'm slowly thinking of making a wrapper that preconfigures that setup as there is pretty much same boilerplate repeated between many projects.
I have been a web engineer for a decade. I've worked on Java Spring apps, PHP, Node.js (express), Python (both flask and Django), clojure, Rust, and now Go. I have worked on teams large and small, and on codebases old and new.
In my experience the problem with this kind of conversation is that you can actually have a pleasant experience in all permutations of the above. Since people have a ceiling on the number of projects they have worked on for a long enough time to have a reasonable opinion on the approach, they come out of this with massive biases and cannot accept that others have had pleasant experiences on the same tools and team sizes if they have had an unpleasant one.
I _promise_ you that you can absolutely have a relatively long-standing codebase without a framework that is a dream to work on and has been touched by many engineers in a huge corporate setting. Similarly I could show you absolutely unworkable travesties that have to eventually be rewritten despite being written with frameworks that claim to prevent this kind of thing occurring.
> I _promise_ you that you can absolutely have a relatively long-standing codebase without a framework that is a dream to work on and has been touched by many engineers in a huge corporate setting. Similarly I could show you absolutely unworkable travesties that have to eventually be rewritten despite being written with frameworks that claim to prevent this kind of thing occurring.
Conversely, you can absolutely have a “relatively long-standing codebase” with a framework “that is a dream to work on and has been touched by many engineers in a huge corporate setting.” And I could show you unworkable travesties that have to be eventually rewritten despite being written in library-only microservices that claim (because “micro”) to prevent this kind of thing from occurring.
Whether selection bias from your experience or an appeal to authority, there isn’t much to your argument. To me it just sounds like comparing well managed to poorly managed codebases.
If I have to manage a team, I’d much rather have the framework laying the groundwork for documenting “how we do things” than rolling consistency out ourselves.
> There is a dedicated team who manages platform tooling including the frameworks/SDKs you use.
Ah, so an organization so bloated that you have entire teams "managing platform tooling" (whatever that means) instead of iterating on features customers actually care about.
When you start to scale for your amount of customers and features, you have to manage caching, job queues, and more. Things frameworks like Rails or Django provide mostly OOB. If you’re using Go or Node, you’ll end up writing your own de-facto framework (even if “writing” means pulling in different libraries and gluing them together) around those features, especially in the case of micro services where you’ll want each of those things available in each service.
Yup, I was the author of go-micro. It was very much a standalone Go framework aka common interfaces grouped together for distributed systems development. By using interfaces they effectively became pluggable abstractions for infrastructure. Unfortunately I don't think a Go library alone solves the problems I was trying to solve so it got merged into Micro which is platform that includes a CLI, API, Runtime, etc. It powers https://m3o.com
We used Micro to build and offer Micro services on M3O. Every API to you see there is powered by the open source equivalent Micro service here https://github.com/micro/services
Different strokes I guess. I've previously worked at big orgs. with Spring, now working with Go microservices and enjoying it by and large.
I don't miss the days where I had to deal with random missing or conflicting beans stopping my app from starting. The nice things about a Go app is because the control flow is so exposed, if it compiles you can be pretty sure it is going to run, and if it doesn't compile you can go to the red in your IDE and fix it.
Adding an endpoint is a matter of updating our OpenAPI file and doing `make oapi-gen` for the code generator we use. I think this is a similar level of effort to doing the same with a mvn or gradle command.
The real difference I notice between Go and Spring microservices though is how much easier it is to dev and debug multiple microservices - it is trivial to compile all of our Go microservices and boot some of them up for debugging without eating all of my laptop's RAM, which is especially relevant if I also want to run them in Kubernetes locally.
I will say I do miss some of the niceties of aspect oriented programming with Spring.
By far my least favourite thing about the Java web ecosystem is how weirdly obscured the bootstrapping process for starting processes is. So much of how your application starts is determined by XML files and DI frameworks that I often have no idea (or am sometimes not even exposed to) where the `main` function is.
Caring about the main function in a framework-ey app generally makes as much sense as caring about the x86 initial boot code when launching mspaint.exe
Not for newcomers to the codebase. Approaching an application that is forced to make the entire application clear as day (as in Go) is so much easier than such Java codebases because I can just read the code and see what it does. This is just my experience though. The Java codebases are always fine as long as I have read several pages of documentation first, which I've learned to do for those cases.
You didn't specify what you meant with "more productive", but if it means "being able to produce more code in less amount of time" then i agree that one can be faster with frameworks/Java.
If you look at the problem more holistically (say "run a reliable service customers want to use") then that metric is just one of many, and more often than not counterproductive vs some other metrics like maintainability and resilience for instance. IME frameworks reflect that and only let you write code fast, or get the hello-world-demo out fast, but neglect the later stages.
I don't know if the conclusion is no framework is better overall, but the frameworks i worked with at least showed mixed results overall.
We're not going to settle this today but I think the later stages are where frameworks shine.
For instance, when Oauth became popular I was maintaining several apps where the original authors had never dreamed of an alternative login method (email and password had been the norm for longer than any of us had worked as developers).
The Rails apps required a few small config changes which I could get from the documentation then HTML for the "Login with Facebook" button.
The framework-less apps were disasters. Each had their own way of doing things. Some used libraries that were no longer maintained, others had rolled their own authentication workflow or half copied the code from another project.
There is a lot to be said for frameworks when facing an unpredictable future. There is a cost to be paid up front as far as learning the framework but the benefits on the back end are enormous.
> The Rails apps required a few small config changes which I could get from the documentation
What Rails configuration would be pertinent to Oauth? Perhaps you mean the application was already using some kind of third-party authentication library (e.g. Devise) that supported Oauth? If that's the case, didn't you just luck out that the developers happened to choose a library that 1. Was still being maintained. 2. Had already added Oauth support?
Most of the Rails apps I have encountered in my career (especially those predating Oauth popularity) used hand-rolled authentication, each different to the next, so it seems that you could have just as easily fell into the same trap you saw elsewhere. It is not clear how Rails saved you here.
When I did Java, I found the Java ecosystem to be very productive because of the libraries available, but Spring was basically useless and was just a way to convert compile-time errors into confusing runtime errors.
Most of the useful stuff in Spring is just thin wrappers over other libraries (including the standard library) anyway.
I expected to see some juicy golang bashing from someone used to sane error handling, pattern matching, and powerful collections libraries. But complaining about "GRPC and Protobuf are overengineered and underdocumented" just doesn't make sense to me.
Those things were basically first implemented in Java. We could argue about subtle differences between Avro and Protobuf but I'm not aware of any credible competitor to gRPC on the JVM in the last 10 years. Thrift and Avro RPC libraries died out years ago, Akka clusters are rather exotic.
Spring works really nice when working on monolithic services, but Golang is on the complete opposite side of the spectrum - Go is really good for microservices.
Spring is more like traditional application development, where you revisit your code multiple time during its long life cycle. In comparison, Golang is more like spit it out fast and forget. Its syntax is so dumb that you can’t possibly misread the code from any angle, so maintenance is also brain dead simple.
In this aspect, using frameworks in Golang simply defeats the point of using the language, because frameworks make codes harder to read. The language is to be used like C, where (almost) everything is manual and explicit.
> In this aspect, using frameworks in Golang simply defeats the point of using the language, because frameworks make codes harder to read. The language is to be used like C, where (almost) everything is manual and explicit.
That's very much a matter of opinion. In my opinion, making every manual and explicit makes it hard to read if you're trying to do anything vaguely high level, because you lose the forest for the trees.
I echo what you're saying. What's ironic is that having worked at an employer that heavily uses golang, they ended up inventing their own dependency injection, app framework, web framework, etc. that are outright terrible, and you end up with the long stack traces that some people like to complain about in Spring or other similar frameworks.
So much money was wasted writing and maintaining those frameworks that was so flabbergasting.
Well there are all sorts of programming. Writing code from scratch to solve problems, writing glue code to connect various libraries, fill in the gaps in code generated by framework scaffolding, drag-n-drop blocks in diagrams and code is generated in the background and so on.
Java/SpringBoot/etc micro services developers have certain way of doing things and they are certainly going to hit lot of problems with Go. Since I have worked in typical Java/J2EE/Microservices project all my career, I think, for most devs coming from larger Java ecosystem, mapping their existing understanding to Go way of doing things is pretty much impossible.
Assuming this means grpc-go, that's a framework. Perhaps your seemingly negative experience with it actually echoes that the best Go framework is no framework, contrary to your opening position?
Very little to do with the fact that it's a Java developer. More someone identifying as "x language developer working at y language shop." Most of the time, including this one, it shows that they see x language as a part of their identity. Hard to have objective conversations if that is the case.
This is one of the better things about Go and its community: eschewing frameworks like this.
Having an ecosystem of in-house libraries tailored to your products' use cases is not the same as developing an "ad-hoc framework". Where I work, we have several languages deployed in our fleet of microservices. A couple have frameworks, and these frameworks are "try to do it all" types, complete with dependency injection and layer upon layer of abstraction intended to help engineers avoid "needless" boilerplate or what have you. They're slow and difficult to debug/troubleshoot for any case that isn't anticipated by the frameworks, and even sometimes then. No, thank you.
Microservice architecture has its own problems, but these problems tend to be easier to reason about than the services themselves that use the frameworks.
> A couple have frameworks, and these frameworks are "try to do it all" types, complete with dependency injection and layer upon layer of abstraction intended to help engineers avoid "needless" boilerplate or what have you.
Isn't this generally the advantage of using open source frameworks? The mature ones have run into most of these roadblocks, and have a solution for it already. And the ones with good taste have escape hatches to allow you to easily bypass for the one they haven't got a solution for. I've heard bad things about Spring, but I'd put forward Laravel as an example of a framework that can do an awful lot for you if you want it to, and gets out of you way when you don't want to use it for a particular task.
That is the value proposition of these frameworks. They work in some cases, and for services/applications where the complexity is generally low. They come with a big restriction, though: a very narrow, highly constrained ("opinionated") way to do everything. The escape hatches don't help--they still have to be within the context of the framework. And the gods help you if anything goes wrong and you have to troubleshoot something that touches any implementation detail of the framework.
I've spent the last year exploring different languages and ecosystems than the one I'm most comfortable in (Python) and what you said absolutely rings true. When you're picking up C#/.NET, there's this almost overwhelming amount of terminology you see getting tossed around that takes you a while to see how it all fits together.
Now that I'm exploring Go, I'm surprised at how many books/learning materials are basically surveys of the features of the language, and then it's basically - "Start building!" "Introducing Go" is just 100 pages for example.
The number of times that the asp.net "startup" has changed is completely ridiculous. It all started from a very bad place with a startup class that is stupidly called by reflection, forcing the developer to use trial and error to discover what methods should be used.
Thankfully, they have finally done away with this but it is sad that they ever allowed these horrible patterns in the first place. Another abysmal area is SignalR (clearly developed by children).
Everyone makes mistakes, even framework/compiler teams. I mean look at how long it took Go to get generics. Huge mistake IMO.
I still dislike what ASP.NET did with their startup, and I don't think it's improved much. The minimal startup is just a bunch of magic (where did the var 'args' come from? Just magic.)
Framework folks are people, they try new things, it doesn't always stick.
main() is a well established entry point in computer science. It’s the first thing one learns when learning to program. Nowhere else in C# are there “ambient” variables.
I suppose it's a quibble, but this annoys me. It shouldn't matter how many layers of abstraction there are, because I should only have to interact with the topmost one. For example when I write vanilla Java there are, off the top of my head, the following layers of abstraction: the Java language, the JVM, Userland, Kernel, C, X64, and CPU microcode. No abstraction is perfect and maybe the day will come where I need to understand the hardware frontend on x64 chips to get my Java program working right, but in the overwhelming majority of cases I only need to worry about the abstraction presented by the Java language itself and maybe on the operations side those of the JVM.
All automated computing abstractions are, in the pathological case, leaky, but there comes a level of leakage where it essentially stops being an abstraction at all, useful or otherwise.
Spring Boot isn't an abstraction, it's a language extension implemented via Java annotations. Thus you must in addition to understanding the relevant parts of Java, also understand the relevant parts of Java/Spring. Personally I think it's poorly documented and doesn't really have a cognitively manageable semantic theory, but it's been a long time since I've worked with it so maybe that's been sorted out?
You’re talking about a different kind/level of abstraction. I’m referring to abstractions built within a language; there isn’t a direct; meaningful comparison to the abstraction of, say, a language over machine code.
Spring Boot isn’t an abstraction, but the language extensions and the things built on them within the frames are (badly engineered ones at that).
You also get to implement stuff like CSRF protection, cookie signing, anti session fixation, etc. You also lose out on community contributions because you are your own community now. You also better have stellar internal documentation because onboarding developers is going to be a pain otherwise.
99% of cases people are better off using a boring conventional web framework and implementing their actual business logic on top of it.
This has always been the sticking point for me when I am wanting to try out new frameworks. When starting a new project I used to get excited on the idea of using a lean barebones framework, only to realize later on the boring bits that I had to figure out/implement or rely on some third-party library that may not stay up to date with best security practices in the future.
"You also get to implement stuff like CSRF protection, cookie signing, anti session fixation, etc. You also lose out on community contributions because you are your own community now."
In Go, not necessarily, because net/http actually is what many languages would call a "minimalist framework".
There's a sense in which pretty much everyone here is right. You do need a framework in the general sense; you can't expect to just throw someone a TCP socket and get reasonable work out of them because the web is very complicated nowadays. But the reason why you can "do without a framework" in Go is that it is already a minimal one of its own, and in particular, that "minimality" focuses on providing a mechanism for composing various bits of HTTP code together.
I think calling Go "no framework" is kind of misleading, because I get what you're saying, but the standard library is not the "no framework" option. The "no framework" option is more like using https://github.com/valyala/fasthttp , which is its own server implementation with an incompatible API that does in fact remove you from the community code, other than the code that works only with that server.
Don't over-read my post. I am merely saying that Go essentially ships with a minimal framework and so it is misleading to say or think of it as being "frameworkless" (and that is a criticism of the original link, yes), not that it is mandatory to use only that "minimal framework" and all other things on top of it are a bad idea. There are times and places to want large, preconfigured packages of additional functionality. But if there are times and places where it is not desirable, Go does come with a minimalist option by default. It is a pretty good, not-very-opionated option suitable for a standard library, where development is slow and there's not much room for experimentation in what a "full" web framework should look like.
The standard library minimalist framework even makes the other frameworks pluggable. You can, for instance, write an API website, then realize that if you need a conventional HTML website, you can plug in a framework for that part, but leave the API part as-is, simply by routing the API URLs to the existing API code while routing the HTML website to the http.Handler provided by the framework. Even "not using an additional framework" is not the commitment it may be in other environments, because the net/http foundation is still there.
Well said. The 'no big framework' thing works for Go because the Go standard library defines a common way for dealing with HTTP. The difficulty, then, is identifying 3rd party packages that play well with the rest of the ecosystem.
You can see the opposite in projects like Echo, Gin, Beego, etc., that eschew the standard library to various degrees and try to build the kitchen sink themselves. Sometimes this works! Echo is very popular, despite having nonstandard handlers and context. An absolute Go newbie is probably going to have an easier time using it than trying to pick out the best collection of libraries themselves.
I agree with you that not using a framework requires you to think more about these things, but if you’re using a routing library like chi or gin there are plenty of libraries adding such functionality to them.
I think that actually depends on the programming language. With golang, such a task is much harder because integration is more errorprone and requires more plumbing, e.g. due to missing generics (until recently).
For junior developers reading this: frameworks are great, but they will become an increasing source of technical debt, performance loss, and frustration. They will allow you to ship your first version faster, but every release after that will become harder. Not that it wouldn't be hard without a framework either, but frameworks make us feel that we need to work within their constraints rather than the constraints of our application and our users.
So learn a framework for whatever language you use, that's useful! But also learn to recognize when you are working against the framework and would be better served with a few libraries that you can combine in the way that you need. Frameworks optimize for certain use cases and expectations, and if your project is no longer aligned with those, it's probably a good idea to think about an exit strategy.
However maybe also learn to love a language that embraces them at the same time so you don't come away with a poor experience because of only trying a language that has poor frameworks (Go, JS/TS, etc).
Definitely try something like Python + Django, Ruby + Rails, Java/Kotlin + Spring to get a feel for what a mature framework can really do.
Then if you find you still don't like frameworks and the style of programming associated with them then sure, find a home in one of the ecosystems that are more inclined to roll it your own way.
Frameworks certainly aren't for everyone but using bad frameworks and then pretending all frameworks are therefore bad is highly prevalent among more recent developers. Try the good ones and then make up your own mind.
Also, this shit is harder than it seems, and it is painfully easy to introduce huge vulnerabilities if you don’t know what you are doing. SQL injections, CSRFs, etc can all be a problem if you go down a naive route.
I was fairly new to "modern" web dev, starting a project, and went with Django because I had more Python experience than anything else. But I found templates very lackluster and poor to integrate with all the different JS ecosystem libraries people are creating to do very cool things on the frontend. Unfortunately, hooking React up to Django is a mess and most people doing this are still 5 years behind in React-land. Are you going to hybrid some template and some React? Are you going to containerize your django and react build system together?
Basically, I wanted a robust frontend ecosystem, well integrated to a simple backend (no Spring, Django-Rest-Framework monstrosities).
That led to React -> Next.js -> Typescript backend -> the rest of the niceties around it like Prisma (ORM-ish)
Create T3 App simplifies all the setup into a template and is extremely fast to start building features. No js build system setup, no python virtual envs. tRPC is an optional nicety.
I don't like using frameworks but I feel this comment is really important. I spent years using frameworks in other languages. Understanding why things are structured the way they are and what works for my needs and what doesn't.
Also when working with a team the value of having a lot of documentation, stackoverflow questions a google search away and an obvious way of doing things seems incredible valuable.
Does building your own microframework brings joy and a sense of accomplishment? yes it does. Does that correlate with accomplishing business needs. Not necessarily.
How about offering real counter advice instead of just saying its bad advice?
Going to have to also agree with the OP. Frameworks are great for the majority of real world cases. There are definitely cases where specific optimizations are required and maybe a framework is not best fit but those are rare.
Lets try to steel man your disagreement though. Having worked in larger orgs I believe there is a trap that some fall in when using frameworks. Ignoring what is happening inside of the plumbing of the framework and ignoring why the framework is doing it. From that perspective you can fall in the trap of not having mastery of domain and it can limit your career in the long run.
On the otherside my argument is that frameworks provide immense value to orgs of all sizes. Even for a single person team there is value because you are not wasting your time on problems that are not part of the business. You should understand how the framework works in the long run though. A lot of these patterns carry over between frameworks and languages, mastering your first one will open a lot of doors in the long run.
"Wasting your time on problems that are not part of the business"
How much time?
Sometimes a task that takes X time and seem unrelated to your "main" task, actually increases your productivity when doing the main task.
A prime example is sleeping when trying to build muscle and lose weight.
On a surface level, sleep is a waste of time: you should be in the gym lifting weights or out there running/cycling/swimming. But actually no. Sleeping improves your results, and improves your performance on your workouts.
I'd argue there's a similar dynamic here.
Working on the lower levels of your problem might seem like a waste of time, but it's actually not. If nothing else, you feel more enjoyment from your work. Being engaged with what you do and not being engaged is huge.
Another problem that often occurs when you are always only working in the "high level" is you have no idea how to do anything well unless the framework does the heavy lifting for you. As soon as you step outside of what the framework was designed to help with, you are lost and have no idea how to approach the problem. All your approaches are half-assed solutions. You see this every single day on "big teams".
For junior developers reading this: frameworks introduce additional complexity (especially ones with obscure dependency injection), learn how to use different tools properly and analyze when they should/should not be used. Learning your language properly (and carefully selecting when a framework makes sense vs when it doesn't) will save you much time and stress when shipping real products.
There's a time and place for framework/no framework, though I'd err on the side of not introducing a huge framework until it's actually needed.
Source: I work on a real, shipped for 3 years, SaaS product primarily written in Go (multiple services). The primary applications are framework-less, but some of our operators make use of kubebuilder (which certainly would qualify as a framework).
For junior developers reading this: frameworks are useful if the tradeoffs they make are right for your current situation. They are good tools when used wisely. However, developers who are heavily invested in a specific framework will often try to sell them as "great" but recognize that their investment biases them and may result in "when all you have is a hammer, everything looks like a nail" syndrome, causing these one-trick ponies to advocate their favored framework even in situations where other frameworks or even no framework is a more sensible solution.
The problem with not using established frameworks for building apps is that you'll wind up creating your own ad-hoc 'framework'. As your app (and the framework) evolves it becomes more feature-rich, it will be difficult for people to ramp up/understand. They won't be able to Google/Stackoverflow for problems they're having because nobody else on earth is using their 'framework'. Then people will want features you've not implemented or thought about, so now you need a framework committee to decide what's in, what's out.
No thanks. Give me ASP.NET Core, Django, Spring Boot for a development organization with more than 1 developer.
Go having a standard library that is so good for building simple services is what sets it aside from all the other languages that have come out in the last while in the same space. It has a great collection of community libraries but I so rarely need to fall back to them as I can usually cobble together whatever I need to pretty easily just using the standard library.
It makes it easy to get started on a project as there is no time i need to spend thinking about the tools im going to use, its just provided all of them for me, in mostly high quality, relatively easy to use implementations of most basic things.
Agreed. But doesn't it seem sometimes as though some modest improvements here & there in the stdlib would have a big payoff in making it immensely more productive ?
What's the value of having sqlx in the stdlib? The only reason I could see is a social one, not a technical one (discoverability).
The Go devs did in the past include external libraries in the stdlib, the most significant one being "golang.org/x/context" moving to "context". This was a very important inclusion because it meant the stdlib could start offering context-aware APIs, and it was a signal to the rest of the ecosystem that the context API is stable forever and other packages can make their APIs context-aware without risk of future breakage. With sqlx, none of these issues arise: the main compatibility interfaces between different SQL libraries (particularly to the drivers providing support for different RDBMS) are already present in the stdlib.
Having such a large standard library to me smells like you can't build one as nice as that with the language. Since it missed generics until recently I can figure out why the STD library is that good and large and why there weren't that much framework. I've used Java before it had generics too.
I'm not sure I understand correctly but I'm assuming that this:
> Having such a large standard library to me smells like you can't build one as nice as that with the language.
means that you're implying that the Go stdlib is written in non-Go because Go would not be expressive enough. If so, that's a wrong assertion. The entire Go toolchain is Go, including the compiler, runtime and stdlib. If you happen to have Go installed, you can find the stdlib at /usr/lib/go/src.
OP doesn't ask the question that would have saved a somewhat straw-man general position on frameworks: Why is it that Go lacks the familiar 'widely adopted framework'?
OP could have written about the two different types of code reuse and why one lends itself better to Go: Libraries vs Frameworks.
Java has magical frameworks because we have a magical JVM and first class (as in baked into the language) metadata utilities at the language level. Java can define & load new objects at runtime. Loose coupling? My god, this is as loose as it gets. In Go, you'll need composition of processes to accomplish the same thing.
Code reuse at Go happens at the library and networked-but-loosely-coupled component levels. This covers a lot of territory but not things that are, in effect, 'containers of components'. If you are in component oriented code sharing territory, Go is the wrong language. With Go, a framework can't do all the magical stuff the other kids' FWs do, but you get most of the pain of using frameworks. It is a language issue.
in general: Partially realized abstractions place greater demands on the language and its runtime capabilities than do partially realized composition (e.g. apis).
This article makes an assumption: you know how to build the application better than framework developers do, which is not the case in general. Django docs are tremendous and you probably do not need most of it but in reality many of them cover real-world cases and do it in a good way. Sure, it may be a question of minimal code to implement it in go, but you have to know that you need this specific feature and you need to know how to implement it.
I've been doing small projects with go for a while and while I enjoy doing it in go (I like the predictability that types bring to me and the fact that I do not need to go through the tomes of docs to figure out something) I still feel every single time that I'm reinnventing the wheel.
The task is simpler with SPAs since all you need from go is a json api, but if you want to build something complete with htmx you'll have to deal with many questions yourself. E.g.
- orm / db access
- Signup / login flow
- Emails
- Static assets versioning
- template helpers
- CSRF?
- Routing and reverse routing
- Page structures (you want to have the same field to look up page title, right?
- Partials for your templates (how do you do form validation in a nice and generic way)
There is a lot of stuff like this. None of this is impossible to solve, it's quite easy in most cases actually, but it adds up and as I said, you have to know how to do it write
One thing for sure is that if you implement the concepts yourself you don't need to abstract them away too much (or maybe you do if you want to reuse all the nice things in your next project). For me the most compelling thing is types and the fact that I'm not dealing with django/laravel magic with long stacktraces that are meaningless to me, but I still return to thinking about doing next project with rails from time to time, just because I've implemented form validation hundred times already.
One good point about frameworks is about limitations - they are there and this is something that's quite hard to overcome at times. Rails framework does things the certain way and if you want to do it differently you have to fight with the framwork or make an attempt to push things to upstream which I'm pretty sure is not the main goal of your project.
There's something to say for getting in the habit of delivering microprojects with minimal dependencies and tooling.
I do most of my Python work with PyCharm on a Windows machine, frequently with poetry as a dependency manager, but lately I was writing a script for burning DTS-audio CDs in a single step on my Linux server (has the optical drive) and made the decision to only use the Python standard library and do it all in a single file I edit with vim. The result is 150 lines of code that spin like a top.
I went through a phase of doing all the problems on HackerRank and learned to love "single-file Java" and discovered that you can do almost anything in a single file except declare a public class (if it is all in one package, default access is all you need.) It's not the usual way I do things but I like Java a lot more knowing I can write simple programs without maven, an IDE and all of that.
Most of my code I write is small projects where all the code is in one or two files
I don't enjoy working on enterprise codebase where there's thousands of files that do very little and the code is refactored so hard that you need to jump between 20 classes to understand how it all works and fits together.
Leetcode solutions are usually single file solutions.
My programming language and interpreter are more than one file though. A parser that lexes and creates an AST and an interpreter class and a class for each kind of AST node that do codegen.
I could probably hast used a single file and inner classes.
After years of working with arguably ever-engineered projects where everything is an interface and the actual implementation is hidden so far up the callstack that half the time was spent searching for the file that needed to be changed, I came to like down-to-earth, YAGNI (You aren't gonna need it) mentality. This includes sometimes keeping things in a single file when sensible.
Sure your domains aren't so pure anymore because you've cut some layers of indirection and simplified things. But the cognitive load is so much smaller.
Such a pleasant delight to work on "dumb" code.
As always, it depends on the size of the project. But my threshold for keeping things simpler has certainly shifted towards trying to keep things simpler when possible.
I literally have a medium-sized repo full of boilerplate required for a normal go web service.
It will be nice to one day factor it out into something I can import versus having to simply copy the template repo.
The stdlib is great but it doesn't give you sessions, orm, csrf, sass/minification, good templating, embedding, etc out of the box. You have to paste in lots of boilerplate to get those working.
Of course the only absolute truth is that there are no absolute truths, so no point of being dogmatic to the extreme.
Still, with experience I came to realise that dependencies carry their own weight, which has to be balanced with the weight of bespoke implementation (which can span the whole spectrum from "obviously" to "NIH")
Yeah, Having built a web application serving 100K+ users in Go. First 2/3 weeks were spent on making sure we were a framework. So new Modules could be added. New external calls were abstracted and also dealt with lot of circular dependency.
This was all fun and learning, but wouldn't mind having a "Flask" or "Express" version of a Golang framework which took care of all the boilerplate out of the box.
We did try some existing solution to no avail like Tiny etc.
But ended up building a non-framework framework anyways especially to support new features/modules and new devs coming it.
Go's standard library already provides functionality that is provided by "frameworks" in other languages. So "no framework" isn't actually a thing.
Also what I like most about Django and Django Request Framework is that it saves me so much writing and reading in almost every case. Not sure how you're supposed to achieve that without something that looks like a framework or how writing ten times the amount of code is more sustainable and maintainable.
> The easiest way is to start by putting everything into one file. You can start simple, defer some decisions, and evolve your project with time.
This is something that most even senior developers don’t grok. Default so called project starter scripts of the node world don’t help either.
The more structure you build before you know wth you’re doing, the more difficult you’ll make life for yourself down the line when you inevitably have to refactor code.
I dislike frameworks and prefer to avoid any dependency but the standard library, but coming from PHP, learning to write web apps in Go feels like being expected to write your own web server. Not sure how I feel about it yet.
I like fiber the most, but can use anything like echo (and past companies use only httprouter, gin, httpkit, etc)
as long as the codebase not overly layered, I guess any framework is fine
only need to split to 3 kind of layer:
1. serialization/transport layer (codegenerated) -- framework goes here
2. business logic layer (one that unit tested), only input struct, transform/process, and output struct (DTOs)
3. persistence/3rd party layer (codegenerated too), add additional go source code file for things that wasteful to be codegenerated, only input struct, basic persistence methods or network calls, and output struct (DAOs) goes here
if using gRPC layer 1 already codegenerated, so only need to fill layer 2 with business logic and codegenerating layer 3.
Your comment made me realize I'm not truly anti-framework, I'm anti-sell-your-architecture-to-the-framework, which most frameworks want you to do.
Problem is, many MANY people do not have sufficient experience to know the perils of putting the framework in charge of the architecture, so they do just that, and decry any other approach as "Java-esque." (That label says an awful lot in and of itself.) The impact of this choice often doesn't become apparent until much farther down the road. Then you start getting into the weird cottage industry involving brittle hacks with DBs/frameworks in order to delay setting them up so that you can run unit tests in under ten minutes.
It's ridiculous.
Part of the blame lies with the devs: refusing to take responsibility for your architecture means you are at the whim of the framework. And the framework devs are also at fault for pushing the lie that you merely need to write the absolute bare minimum of code and everything else will be taken care of.
Limiting the scope of what is exposed to the framework keeps you in control of the architecture.
This is probably why I find almost every framework deeply unsatisfying. Gin, in Golang, doesn't bother me much for whatever reason, but it doesn't seem to aspire to the same number of things as other frameworks.
I can't tell if the article is recommending not using 3rd party packages or simple not to get caught up in an opinionated framework. I agree that opinionated frameworks don't can be problematic as they don't compose but can't agree that with the statement that 3rd party libraries are bad. This is basically build vs buy argument favouring the build side.
A more measured approach would be to avoid 3rd party packages that aren't very popular or are not particularly active.
It is recommending the latter. Instead of using an opinionated framework like buffalo or revel, they are recommending either using the standard lib or single purpose libraries like Chi for routing.
I love writing no framework/no lib backends in nothing but Node.js and TypeScript with some fp-ts and io-ts on top of that.
Node's standard libraries (http, fs, etc) give you already enough to work with.
Sure, you're going to have to re-implement plenty of things (e.g. file serving, which anyway isn't great in node), and many other things but there's plenty of pros:
- you can always understand how things work and change them to suit your use cases. That's just not the case with external tools. Good luck figuring out the codebases or even be able to build them locally
- it's much better for educational purposes to understand how things fit together and are supposed to work
The biggest con: security. Libraries and frameworks likely patch many vulnerabilities you didn't even know existed. This alone pushes me off from this approach and instead I fall back to some bloated framework, but security is important.
The second biggest con: performance. You're likely gonna do plenty of performance mistakes if you don't have a good knowledge of how Node really works.
The main beef I have with frameworks isn't actually with the framework. It's teams fork lifting / re-writing to the "next best thing" to fix a problem instead of polishing what they have.
You wind up with a perpetual revolving door of immature implementations instead of incrementally improving something with known flaws.
Having no framework comes with having no documentation.
If you select a framework and work in the same scope as the framework intends to, then you can onboard new engineers much more easily by telling them: We are working as the framework defines, look into the documentation and you will understand the basic architecture of our software.
Of cause, devs can maintain their own documentation, but I guess this usually is something that is not done very well.
My personal experience: As soon as a company started their software without a framework, it became a huge mess. While onboarding, I observed very bad architectural decisions and even very severe security issues.
I'd say, only go with no framework, if you are experienced enough to technically create an own (good) framework.
From my observations, going without a framework was usually a decision done by young engineers.
Go without a framework and just the standard lib is already about equivalent to Python with FastAPI.
All you need to do is add a logger of choice, which will be different based on your architecture (one / multiple servers) anyway.
You might want to add user auth, which is often an external server anyway.
Maybe you want some boilerplate / codegen tool to add/remove/select objects from a database. But I personally don't use that because it's hard to optimize performance with those tools and SQL is already a good tool for it imho, don't need an extra abstraction.
In summary, all you need is the standard lib and maybe 3 external libs in Go, because it was made in a time when an api was already pretty standard.
In other languages you need way more extra stuff to create an api, so the need for a framework is greater.
I think this is the thing with (web) frameworks. Are you really going to remember that? And actually do it? And work around and build those other gnarly things for CSRF, SSRF and XSS? If you only need to serve happy-case HTTP, for sure you're better of without a framework. At least my experience without using a framework is that it's a tad annoying when you need the extras. And requires a lot deeper understanding of things as you don't abstract it away which is good for learning, but might be cumbersome for actually shipping things.
For security/safety sensitive tasks, you should be using checklists e.g. [1] so you don't need to remember. Pilots use pre-takeoff checklists to reduce chances of human error. Likewise, you shouldn't assume the framework will give you proper defaults.
Is broken streaming a thing? A quick search shows some issues with gin's own gzip middleware. Don't know if that's also the case behind a proxy with gzip middleware and gin's disabled.
Looking at the newest code I also noticed you can exclude paths from gin's gzip middleware, if a proxy is not an option.
Been writing Go for my own pet projects and some production side projects for two years. Often resorted to "small libraries" like Chi, or SQL query builders.
Since GitHub Copilot, I ditched SQL query builders as all the scanning and repetitive work is handled nearly immediately. I also ditched Chi and handle routing myself. Same for logging.
The issue is spending time doing things that took one line before with a framework (e.g., parsing URL path params). But it's actually satisfying.
What does a typical handler look like in this scenario (url path params). I usually end up using Gorilla mux for this, but standard library for everything else.
I create a simple func for each handler to parse params. Most urls params are properly structured as /resources/{resourceID}/subresources/{subresourceID}.
So I can essentially create a map[string]string by splitting slashes and going 0 = key, 1 = value, 2 = key, 3 = value, etc.
If a handler uses a different structure like /resources/{resourceID}/{subresourceD}, I will have the func specifically handle that case.
Some ways may work better but this works for me and it's not magical, and I tend to touch that code once and never again so it's then out of the way, all in a file.
> Python has Django and Flask, Ruby has Rails, C# has ASP.NET, Node has Express, and PHP has Symfony and Laravel. Go is different: there is no default framework.
And this is the root of most problems described by author. If I start project on Python - I know that I can take Django and use it for at least 4 next years, spending one sprint a year tops to migrate to next LTS version. In Go only standard library currently gives me that kind of reliability.
I'm maintaining about half a dozen microservices written in Go. In my experience, the Go community is very good about providing backwards compatibility.
In fact, I'm very strict about keeping on top of my dependencies because "spend a sprint to upgrade to the next version" is a process smell to me, no matter how seldomly it occurs. I have my Renovate bot set up to send PRs every Friday. Most of them are on "automerge on test success" at this point, then they soak in QA for the weekend and I send them to prod on Monday. The only trouble I can recall is with updating Kubernetes libs (let's not open that can of worms right now) and in some cases I have to manually intervene when libs upgrade from `interface{}` to using generics, but that's a refactoring that I gladly do.
Wired: Browser WASM (from Go source) that accepts commands over websockets to modify & render the DOM. (Is this "client hydration"?) Tight integration with htmlx (for pizazz + ruthless efficiency) and Javascript (for rich text editors + other advanced widgetry). This may require full Go, if tinygo continues to lack reflection support.
Yeah, I think it might be a good option for experienced devs and it can also turn into a rabbit hole for junior folks. No silver bullet and everything depends on what you are trying to build. But yeah this discussion never get's old :)
The graphic in OP summerize it pretty well. If all you need is something quick, and you don’t expect (or currently have the budget) to scale it a lot in the close future, then using a framework may very well be the best solution.
It seems to me too many don't know how big is Spring framework. You can do basically everything with OOTB starter. Enterprise projects are a breeze to develop with Spring Boot.
We are more people, we are more experienced and have a decent engineering culture. Yet we're definitely less productive than the last average, traditional Java Spring Enterprise team I've been in. "Circumventing" a framework restriction (which rarely happens if you stick to good practices) is MUCH less effort than building things yourself from scratch.